2009-06-01 4 views
13

私はこのようなリポジトリパターンを使用するアプリケーション、その後、個別のサービス層を設計しました:あなたが見ることができるように、私はRegisterメソッドの内部で、私のリポジトリをインスタンス化していますTDD - 私のサービスレイヤーを偽のリポジトリでテストしたいですが、どうですか?

public class RegistrationService: IRegistrationService 
{ 
    public void Register(User user) 
    { 
     IRepository<User> userRepository = new UserRepository(); 
     // add user, etc 
    } 
} 

。今では単体テストを書いてみたいので、実際にそれを手に入れて偽のリポジトリに置き換えることはできません。

リポジトリをクラス変数として追加したくないのですが(コンストラクタで設定する)、コードが「臭い」になると思います(すべてのリポジトリがすべてのメソッドに必要なわけではなく、リポジトリなどを知るために呼び出しレイヤーが必要です)。

提案?

+1

正しいパスを使用していますが、コンストラクタを介して依存関係を渡しても、コードが邪魔になることはありません。これは受け入れられた設計主体(制御の反転)です。 – ebrown

+0

これはDIを使った単体テストに関する本当の話です:http://www.youtube.com/watch?v=wEhu57pih5w –

答えて

16

依存性注入を使用する必要があります。 UserRepositoryは、RegistrationServiceクラスの依存関係です。クラスを適切にテスト可能にする(つまり依存関係を分離して)ためには、依存関係の作成を制御するものを「逆転」する必要があります。現在、あなたは直接コントロールを持ち、内部でそれらを作成しています。ただ、そのコントロールを反転し、(城ウィンザーのようなIoCコンテナなど)外部の何かをできるようにそれらを注入:

public class RegistrationService: IRegistrationService 
{ 
    public RegistrationService(IRepository<User> userRepo) 
    { 
     m_userRepo = userRepo; 
    } 

    private IRepository<User> m_userRepo; 

    public void Register(User user) 
    { 
     // add user, etc with m_userRepo 
    } 
} 

私は追加するのを忘れだと思います。あなたの依存関係を注入できるようになると、それらを嘲笑する可能性が広がります。 RegistrationServiceはその従属ユーザーリポジトリをそのコンストラクタへの入力として受け入れるので、実際のリポジトリではなくモックリポジトリを作成して渡すことができます。

+0

これは私がIResositoryをクラス変数として持つことを望んでいませんでしたが、注入についてはコンストラクタに行き過ぎる方法だと思います。ありがとう! – Alex

+0

レポ自体を注入するのではなく、ファクトリを注入して、それから要求に応じてレポを作成することができます。 – jrista

0

IOCコンテナを使用し、別のリポジトリを登録することができます。

9

依存性の注入は、物事のこれらの種類のために本当に便利になることができます。

public class RegistrationService: IRegistrationService 
{ 
    public void Register(User user) 
    { 
     this(user, new UserRepository()); 
    } 

    public void Register(User user, IRepository<User> userRepository) 
    { 
     // add user, etc 
    } 

} 

あなたがデフォルトとしてUserRepository使用する能力を取得し、(テスト用モックやスタブ)カスタムを渡すこの方法をIRepositoryあなたが必要とするものは何でも。 2番目の登録メソッドのスコープを変更して、すべてに公開したくない場合もあります。

上記の例よりもさらに分離された構文を購入するIoCコンテナを使用することも、さらに優れた方法です。

public class RegistrationService: IRegistrationService 
{ 
    public void Register(User user) 
    { 
     this(user, IoC.Resolve<IRepository<User>>()); 
    } 

    public void Register(User user, IRepository<User> userRepository) 
    { 
     // add user, etc 
    } 

} 

あなたはroll your ownあり得る他の人の答えによって記載されているとして、多くのIoCコンテナは、そこにいる、または:あなたはこのような何かを得ることができます。

+0

これはかなりうまく見えますが、私はuserRepositoryパラメータで2番目のメソッドを作成する必要があるという欠点があります。 RegistrationServiceのすべてのメソッド... – Alex

+1

@Alexそれは正しいです。もしあなたがそうしたくなければ、あなたはコンストラクタにリポジトリを注入することもできますが、あなたはあなたの質問でそのことを躊躇していました。 – Joseph

+0

@Alexしかし、メソッドの1つは "デフォルト"の振る舞いを定義するためのラッパーであり、実際の振る舞いはありません。 – Joseph

0

私は、リポジトリをクラスパラメータに移動し、必要に応じてIoCコンテナを使用してリポジトリを注入することを提案します。このようにして、コードは単純でテスト可能です。

2

あなたがしなければならないことは、Inversion of ControlまたはDependency injectionを使用することです。

あなたのクラスは、他のクラスの実装、特にこのようなレイヤーに縛られるべきではなく、ユーザーリポジトリをコンストラクタまたはプロパティとして表すインターフェイスを取る必要があります。それが実行されると、具体的な実装を提供し、それを使用します。リポジトリインタフェースを実装し、テスト時に提供する偽物を構築することができます。

非常に多くのIOCコンテナ、Unity、Ninject、Castle Windsor、およびStructureMapがあります。


は、私はちょうどあなたのユニットテストの間に、明確になりたかった、私はあなたが実際に使用 IOCコンテナにしたいと思うことを知りません。統合テストは確かですが、単体テストでは、テスト中のオブジェクトを新規に作成し、そのプロセス中にあなたの偽物を提供してください。 IOCコンテナは、少なくともIMOで、アプリケーションまたは統合テストのレベルで手助けし、そこでの設定を容易にします。

単体テストは「純粋」なままで、テストしようとしているオブジェクトだけで動作するはずです。少なくともそれは私のために働くものです。

1

IOCが基本的に動作する方法は、XをYと置き換えるように指示することです。IRepositoryを受け入れるだけです(他の人が述べたように)をパラメーターとして使用し、IOC内でIRepositoryをMockedRepository(例えば)と置き換えるように指示します。

web.configの初期化を可能にするIoCを使用すると、devからprodへの移行が非常に円滑になります。設定ファイル内の適切なパラメータを変更するだけで、コードに触れる必要はありません。後で、別のデータソースに移動することを決定した場合は、それを切り替えるだけで自分の道に迷ってしまいます。

IoCは楽しいものです。

IOCインジェクションをしなくても、単体テストを行いたい場合は、それを行うことができます。あなたは、IRepositoryを受け入れるようにコンストラクタをオーバーロードし、ちょうどそのようにあなたの模擬レポを渡すことでこれを行うことができます。

+0

IOCに関する追加情報ありがとうございました:) – Alex

1

私は何かに取り組んでいます。まあ、現時点ではほぼそのままです。私は私のIUserDataRepositoryのプロキシインスタンスをモックアップするためにMOQを使用しています。テストの観点から

//arrange 
var mockrepository = new Mock<IUserDataRepository>(); 
// InsertUser method takes a MembershipUser and a string Role 
mockrepository.Setup(repos => repos.InsertUser(It.IsAny<MembershipUser>(), It.IsAny<string>())).AtMostOnce(); 

//act 
var service = new UserService(mockrepository.Object); 
var createResult = service.CreateUser(new MembershipUser(), "WebUser"); 

//assert 
Assert.AreEqual(MembershipCreateUserResult.Success, createResult); 
mockrepository.VerifyAll(); 

MOQはまた私のUserServiceのは、そのような状況下で試験することができるようにだけでなく、特定の例外をスローするために使用することができます。

他の人が述べたように、IoCは必要な統制を処理します。この場合、UserServiceクラス用の実際のリポジトリを作成します。