web-dev-qa-db-ja.com

MVC 3:NUnit、Ninject、Moqでテストする方法を学ぶには?

私の質問の短いバージョン:

  1. NUnit、Ninject 2、Moqを使用して、MVC 3アプリケーションにテストを実装する方法を学ぶことができる、いくつかの優れた詳細なソースに誰かが私を向けることができますか?
  2. Controller-Repositoryのデカップリング、モッキング、および依存関係の注入がどのように連携するかを、ここにいる誰かが明確にしてくれるでしょうか?

私の質問の長いバージョン:

私がやろうとしていること...

Entity Framework 4を使用するMVC 3アプリケーションの作成を、データベース優先のアプローチで現在始めています。私はこれを正しく行いたいので、クラス、レイヤーなどをテストしやすいように設計しようとしています。ただし、ユニットテストや統合テストについては、学問的な理解以外にはほとんど、またはまったく経験がありません。

多くの研究の後、私は使用することに決めました

  • NUnitテストフレームワークとして
  • Ninject 2依存関係注入フレームワークとして
  • モックフレームワークとしてのMoq.

どのフレームワークが最適であるかなどのトピックがこれに入る可能性があることは知っていますが、現時点では、確かな意見を形成するのに十分な知識はありません。だから、私はこれらの無料のソリューションを使うことに決めました。

これまでに学んだこと...

私はいくつかの時間を費やして、このようなものを処理し、次のようなリソースを読みました:

これらのリソースから、コントローラーとデータアクセスロジックを分離するために、リポジトリインターフェイスを備えたリポジトリパターンの必要性をうまく管理することができました。その一部をすでにアプリケーションに書き込んでいますが、全体のメカニズムについて、およびこのデカップリングをモッキングまたは依存性注入、あるいはその両方をサポートするために行っているのかどうかは明確ではありません。そのため、皆さんからこの件について聞いても構わないと思います。この点について私が得ることができるどんな明確さも、この時点で私を助けます。

どこがぼやけてきたのか...

上記の テスト可能なASP.NET MVCアプリケーションの構築 で説明されているように、Ninjectに頭を巻き付けようとするまで、私はこのことをかなりよく理解していると思いました。具体的には、筆者がドキュメントの半分ほどのところでサービスレイヤーの実装について説明し始めた時点で、私は完全に迷いました。

とにかく、私は今、私が理解できるようになるまで、これらのものについてさまざまな視点を得るように試みるために、より多くのリソースを探しています。

これらすべてを要約し、具体的な質問に要約すると、私は次のことを考えています:

  1. NUnit、Ninject 2、Moqを使用して、MVC 3アプリケーションにテストを実装する方法を学ぶことができる、いくつかの優れた詳細なソースに誰かが私を向けることができますか?
  2. Controller-Repositoryのデカップリング、モッキング、および依存関係の注入がどのように連携するかを、ここにいる誰かが明確にしてくれるでしょうか?

編集:

Githubで Ninject公式ウィキ を見つけたので、それを調べて、私にとって明確になっているかどうかを確認します。しかし、私はまだSOコミュニティのこれに関するすべての考えに非常に興味があります:)

48
campbelt

Ninject.MVC nugetパッケージを使用している場合、混乱を引き起こしていたリンクした記事の一部は必要ありません。このパッケージには、コントローラーの注入を開始するために必要なものがすべて含まれています。これがおそらく最大の問題点です。

このパッケージをインストールすると、App_StartフォルダーにNinjectMVC3.csファイルが作成されます。このクラス内にはRegisterServicesメソッドがあります。ここで、インターフェースと実装の間にバインディングを作成する必要があります

private static void RegisterServices(IKernel kernel)  
{  
  kernel.Bind<IRepository>().To<MyRepositoryImpl>();
  kernel.Bind<IWebData>().To<MyWebDAtaImpl>();
}        

これで、コントローラーでコンストラクターインジェクションを使用できます。

public class HomeController : Controller {  
    private readonly IRepository _Repo;
    private readonly IWebData _WebData;

    public HomeController(IRepository repo, IWebData webData) {
      _Repo = repo;
      _WebData = webData;
    }
}

テストカバレッジが非常に高い場合は、基本的に、ある論理的なコード(コントローラーなど)が別の(データベースなど)と通信する必要があるときはいつでも、インターフェイスと実装を作成し、RegisterServiceにバインディングバインディングを追加し、新しいコンストラクター引数を追加します。 。

これはコントローラーだけでなく任意のクラスに適用されるため、上記の例で、リポジトリの実装にWebDataのインスタンスが必要な場合は、読み取り専用フィールドとコンストラクターをリポジトリの実装に追加します。

次に、テストに関しては、必要なすべてのインターフェイスのモックバージョンを提供する必要があるため、テストしているのは、テストを記述しているメソッドのコードだけです。したがって、私の例では、IRepositoryに

bool TryCreateUser(string username);

コントローラーメソッドによって呼び出されます

public ActionResult CreateUser(string username) {
    if (_Repo.TryCreateUser(username))
       return RedirectToAction("CreatedUser");
    else
       return RedirectToAction("Error");
}

ここで実際にテストしようとしているのは、ステートメントと戻り値の型が、特別な値に基づいてtrueまたはfalseを返す実際のリポジトリを作成する必要がないことです。これはあなたがモックしたいところです。

public void TestCreateUserSucceeds() {
    var repo = new Mock<IRepository>();
    repo.Setup(d=> d.TryCreateUser(It.IsAny<string>())).Returns(true);
    var controller = new HomeController(repo);
    var result = controller.CreateUser("test");
    Assert.IsNotNull(result);
    Assert.IsOfType<RedirectToActionResult>(result)
    Assert.AreEqual("CreatedUser", ((RedirectToActionResult)result).RouteData["Action"]);
}

^私はxUnitをよく知っているので、コンパイルできません。また、頭の上からRedirectToActionResultのプロパティ名を覚えていません。

まとめると、あるコードを別のコードとやり取りしたい場合は、その間のインターフェースを強打します。これにより、2番目のコードをモックして、最初のコードをテストするときに出力を制御し、問題のコードのみをテストしていることを確認できます。
これで本当に私にペニーの低下をもたらしたのはこの点だと思います。コードが要求するのではなく、テストが要求するので、これは必ずしもそうです。

MVCに固有の最後のアドバイスの1つは、基本的なWebオブジェクト、HttpContext、HttpRequestなどにアクセスする必要があるときはいつでも、これらすべてをインターフェース(この例ではIWebDataのように)でラップしますが、基本クラスは、モックする必要のある内部依存関係がたくさんあるため、非常にすぐに苦痛になります。
Moqの場合も、モックを作成するときにMockBehaviourをStrictに設定すると、モックを提供していないものが呼び出されているかどうかが通知されます。

60
Chris Sainty
  1. これが私が作成しているアプリケーションです。オープンソースでgithubから入手でき、必要なものをすべて利用しています-MVC3、NUnit、Moq、Ninject- https://github.com/alexanderbeletsky/trackyt.net/tree/master/src =

  2. コントローラーとリポジトリーの分離は簡単です。すべてのデータ操作はリポジトリに移動されます。リポジトリは、IRepositoryタイプの実装です。コントローラはそれ自体の内部に(new演算子を使用して)リポジトリを作成するのではなく、コンストラクタの引数またはプロパティによってそれらを受け取ります。

public class HomeController {
  public HomeController (IUserRepository users) {

  }
}

この手法は「制御の反転」と呼ばれます。制御の反転をサポートするには、いくつかの「依存性注入」フレームワークを提供する必要があります。 Ninjectは良いものです。 Ninject内で、特定のインターフェースを実装クラスに関連付けます。

Bind<IUserRepository>().To<UserRepository>();

また、デフォルトのコントローラーファクトリーをカスタムコントローラーファクトリーに置き換えます。カスタムの内部では、Ninjectカーネルへの呼び出しを委任します。

public class TrackyControllerFactory : DefaultControllerFactory
{
    private IKernel _kernel = new StandardKernel(new TrackyServices());

    protected override IController GetControllerInstance(
        System.Web.Routing.RequestContext requestContext,
        Type controllerType)
    {
        if (controllerType == null)
        {
            return null;
        }

        return _kernel.Get(controllerType) as IController;
    }
}

MVCインフラストラクチャが新しいコントローラーを作成しようとすると、呼び出しはカスタムコントローラーファクトリのGetControllerInstanceメソッドに委任され、それがNinjectに委任します。 Ninjectは、そのコントローラーを作成するために、コンストラクターにIUserRepository型の引数が1つあることを認識しています。宣言されたバインディングを使用すると、「IUserRepositoryのニーズを満たすためにUserRepositoryを作成する必要がある」ことがわかります。インスタンスを作成してコンストラクタに渡します。

コンストラクターは、内部で渡される正確なインスタンスを認識しません。それはすべて、そのために提供するバインディングに依存します。

コード例:

9

ご覧ください:DDDメルボルンビデオ- 新しい開発ワークフロー

ASP.NET MVC 3開発プロセス全体が非常によく提示されました。

私が最も気に入っているサードパーティのツールは次のとおりです。

  • NuGetを使用してNinjectをインストールし、MVC3フレームワーク全体でDIを有効にする
  • NuGetを使用してnSubstiteをインストールし、モックを作成して単体テストを有効にする
1
Vincent