ASP.NET MVCのユニットテストを実装するときに考えたこと


ASP.NET MVCでテストを書く際に考えたことのメモ

おすすめのパッケージ

Moq

テストのためのダミーオブジェクトを作成するパッケージ。

簡単な使い方

var mock = new Mock<IHelloModel>();
mock.Setup(x => x.Hello()).Returns("test return");

IHelloModel hello = mock.object;

ChainingAssertion

MsTestも手軽にいい感じにかけるパッケージ。

test = "test";
test.Is<string>("test"); // 成功

コントローラのテスト

とりあえず、コントローラのテストを書いてみた。

public class HelloControllerTest
{
    [TestClass] /* テスト対象群であることを表す */
    public class Index /* テストしたいメソッド名 */
    {
        private HogeController controller;

        [TestInitialize] /* テストの初期化処理毎。テスト毎に実行される */
        public void TestInitialize()
        {
            // コントローラを初期化
            this.controller = new HogeController();

            // Indexアクションを実行する
            var result = await this.controller.Index() as ActionResult;
        }

        [TestMethod] /* テスト対象 */
        public async Task 一覧画面が表示されること()
        {
            // 結果オブジェクトがどうなっているかを確認する
            result.IsInstanceOf<HogeResult>();
        }
    }
}

テストしやすいコード

とにかく DI (Dependency Injectyon) する

class HogeController
{
  private IDependencyClass _Fuga { get; set; }

  // 引数がないときは新しいインスタンスを注入する
  public HogeController() : this(new DependencyClass()) { }
  public HogeController(IDependencyClass fuga)
  {
    this._Fuga = fuga;
  }
}

そうするとモックテストがはかどる

...
// Helloメソッドが"hello"を返すモックを作る
var mock = new Mock<IDependencyClass>();
mock.Setup(x => x.Hello()).Returns("hello");

// 作ったモックを注入したコントローラーをテストできる
var controller = new HogeController(mock.object);
var result = controller.Index() as ActionResult;
...

意識したいこと

  • DI
    • メソッドが利用しているクラスと結合しすぎてしまうため、インスタンスの生成処理は極力内部で行わない
    • 他クラスを参照する場合は、DIで利用することを意識する
    • DIモジュールをインタフェースで定義する
  • 単一責任
    • 一つのメソッドに色々な処理を含めず(ifでの分岐を減らす)、責任を一つにしてメソッドを分ける
    • メソッド名と処理内容が一致しやすく読みやすくなる
  • 担当の明確化
    • Sessionやリクエストの状態はControllerに任せる(Model側に意識させない)
    • ModelはDBと切り離しなるべくプレーンなクラスにする(POCO)。DBを意識させない
    • Repositoryパターンを使うといい?かも
    • ViewModelは表示に関する処理のみ記述する。値の生成などはModel側でやりたい
  • Ruby on Rails的な思想
    • DRY (Don't repeat yourself)
    • 同じ処理は2度書かない
    • レールに乗る (ベストプラクティスから外れないようにつくる)
    • 他の人が読みやすいコードになる
    • Rails程レールはないが、規約決めるといいかも
  • 個人的な見解
    • 性能より保守性
    • LINQやEntityFrameworkを積極的に使いたい
    • コードファースト
    • Dbの構造のコード化
      • DRYにもつながる
    • 自動テストの利点
      • 変更に強くなる
      • コードが読みやすくなる

その他

MSTestを全く触ったことがないので、他にもいいパッケージなど教えてほしいです...