テスターですが何か?

ホビープログラマ略してHPです

ASP.NET MVCのクラスの作り方を考えてみた(2)

leave a comment »

前回のエントリーでざっくりしたクラス構成を作成しました、今回はそれに対する単体テストコードを作成します。

前回作ったASP.MVCのコードは以下の構成になっていました。

  • Controllerが呼び出すコーディネーター的な役割のWorkerServiceクラスを作成
  • DI(コンストラクタインジェクション)でインターフェースと実装を分離

今回単体テストを実装するにあたっての前提は以下のとおりです

  • 単体テストフレームワークはMSTestを使用します(Visual Studio 2012 Express for Webに同梱)
  • Mock用フレームワークはMoqを使用します

本当、ホビープログラマーがVisual Studioから単体テストを実行できる時代が来るとは思いませんでした。Visual Studio 2012 Expressには単体テスト機能が含まれています、Visual Web Developer 2010+Nunitで開発するなら、Visual Studio 2012 Express for Webを使いましょう。

まずはNugetを使ってソリューション作成時にあわせて作成したテストプロジェクトにパッケージをインストールします。

PM> Install-Package Moq
‘Moq 4.0.10827’ は既にインストールされています。
‘Moq 4.0.10827’ が MvcHogeApp.Tests に正常に追加されました。

まずはControllerのテスト(HogeControllerTest.cs)を作成します

[TestClass]

public class HogeConrtollerTest

{

    // Moqを使ったテスト

    [TestMethod]

    public void TestMethod1()

    {

        // mockの作成

        var Mock = new Mock<IHogeWorkerService>();

        Mock.Setup(m => m.MakeHoge()).Returns(new HogeViewModels

        {

            HogeId = 1,

            HogeName = "HogeHoge"

        });

 

        // アクションメソッドの呼び出し

        var Target = new HogeController(Mock.Object);

        var Result = Target.Index() as ViewResult;

        var ViewModel = Result.ViewData.Model as HogeViewModels;

 

        // assert

        Assert.AreEqual(1, ViewModel.HogeId);

        Assert.AreEqual("HogeHoge", ViewModel.HogeName);

    }

}

ServiceWorkerクラスは各種ビジネスロジッククラス、データアクセスクラスなどを呼び出します。そのため、単純にServiceWorkerクラスのメソッドを呼び出してしまうと、それら各種ビジネスロジッククラス、データアクセスクラスの都合に引っ張られてしまいます。簡単な例としては単体テストを実行する環境にデータベースが作成されていて、テーブルが作成されていて、テストの条件に必要なデータが全部揃っている必要があります。また、呼び出し先のクラスメソッドが実装されていて、そのクラスにはバグがない状態でなければServiceWorkerクラスのテストができません。ServiceWorkerクラスのバグなのか、呼び出し先のクラスのバグなのか切り分けが必要になってしまいます。

そのため、呼び出し先クラスのmockを作成して、テストされるクラスにとって都合のいい(テスト条件に合った)固定の戻り値を返すように設定します。この場合はHogeWorkerService.MakeHogeメソッドの戻り値HogeViewModelsが固定値になるようにします。そしてこのモックをテスト対象クラスのコンストラクタの引数に指定します。これが、前回DIを採用した理由です。

ネット上にあふれている単体テストの記事を見ていると、単体テストができそうな気がするかもしれません。ただ、クラスの呼び出しは階層化されており単体テストをしたいクラス・メソッドから呼び出しているメソッドの戻り値を考慮しなければならず、これが現実の単体テストを難しくしている、ハードルを高くしている原因だと思います。なので(自分の身の回りでは)、単体テストは行われていません、UIを操作して手動でテストしたほうがはるかに簡単と考えられています。しかし、MockとDIを採用することで現実的な難しさまでハードルを下げることができると思います。

そして、WorkerSeriveの単体テストコードはこのようになります。

[TestClass]

public class HogeWorkerServiceTest

{

    [TestMethod]

    public void TestMethod1()

    {

        // mockの作成

        var Mock = new Mock<IHogeDao>();

        Mock.Setup(m => m.GetHogeList()).Returns(

            new List<HogeModels>

            { new HogeModels { Id=1, HogeName="HogeHoge"}}

        );

 

        // メソッドの呼び出し

        var Target = new HogeWorkerService(Mock.Object);

        var Result = Target.MakeHoge();

 

        // assert

        Assert.AreEqual(1, Result.HogeId);

        Assert.AreEqual("HogeHoge", Result.HogeName);

    }

}

現実のクラスはMock作成時の戻り値設定はこんなに簡単には行かないでしょうが。それでも「単体テスト」といいつつ、階層的に呼び出しを行ってテストをすることに比べれははるかに簡単になると思います。

今回は単体テストをテーマに書きました。作成したソースはSkyDriveにアップしてあります。「MvcHogeApp_v2」をダウンロードしてください。サイズを抑えるため、アセンブリ類は削除してあるのでVisual Studio 12 Express for Webがインストールされた環境で開いてください。

次はUnityを使ったDependectyResolverについて書きたいですね。

// なんで単体テストクラスのテンプレートに

// using System.Collections.Generic;

// using System.Linq;

// が含まれていないんだろう?軽くはまりましたわ。単体テストクラスのテンプレートはかなりすっきりしたのに、ちょっと残念

Written by david9142

2012年7月14日 @ 6:22 PM

カテゴリー: ASP.NET MVC

Tagged with ,

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中

%d人のブロガーが「いいね」をつけました。