SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

ASP.NET MVC3入門

ASP.NET MVC 3における単体テストの基本と記述方法

ASP.NET MVC3入門(6)

  • X ポスト
  • このエントリーをはてなブックマークに追加

リポジトリクラスの単体テスト

 前回DI実装したサンプルでは、PubsRepositoryクラスでインターフェースを実装し、HomeControllerクラスやPubsControllerクラスにDIを実施しました(図5)。

図5 前回作成したサンプルプロジェクト概要図
図5 前回作成したサンプルプロジェクト概要図

 DI実行前のSendMessageメソッドが理想通りの動作をするかテストを実施します。SendMessageメソッドは文字列内にメソッドにアクセスした日時を付加し、文字列として値を返すメソッドです。

 実際のテストコードは以下のとおりです。

SendMessageTestメソッドの記述例
[TestMethod]
public void SendMessageTest() 
{
    //// 準備 理想値の設定
    string expected = String.Format("現在の日時は{0}です。これはDIの実装例です。", System.DateTime.Now.ToString());
    string actual;
    PubsRepository repository = new PubsRepository();

    //// 実行 SendMessageメソッドの実行
    actual = repository.SendMessage(System.DateTime.Now.ToString());

    //// アサート
    Assert.AreEqual(expected, actual);

}

 テストコードを見てお気づきになるかもしれませんが、実はDI実装しているSendMessageの単体テストは通常のメソッドの単体テストと変わりありません。

 上記テストコードでは意識しづらいかもしれませんが、細分化されたメソッド単位でみると依存関係がないためテストがしやすくなります。

 しかし、第4回で記載したようにDIは依存関係を分離していく過程でクラスやインターフェースが増大しプロジェクト全体が見渡し辛くなることもあります。依存関係の低下によるテストの容易さと、全体の見渡し辛さを比較した上でプロジェクトに合うほうを選択すると良いでしょう。

 また、DI使用時のテストの留意点としてすべてのアプリケーションに共通して言えることですが、設計をしっかりするという一言に尽きます。

 DIはやろうと思えば場凌ぎ的なパッチの要領でメソッドなどを付加していくことができてしまいます。このような形で追加していくことは結果としてテストを容易にできるメリットを打ち消してしまうので、留意しましょう。

テストコードの記述時の心構えについて

 ちょっとしたTipsになりますが、テストコードは自分以外の開発者が見ても、このテストは何をするロジックなのか? 何をインプットし、何がアウトプットされ、どうやって確認するのか? などの情報をテストコードだけで視認できるような記述を心がけることがコードの品質を向上させるポイントになります。例えば、上記準備ステップの段階で、理想値expectedに値を設定しています。actualは実行の段階で取得されますが、上記を見るだけで取得したい値は「今の日時情報が付加された文字列」ということ、文字列同士を比較したいテストだということも相手に伝えることができます。

 テストコードはメインのロジック以上に可読性が要求されるため、相手に伝わる記述やコメントを意識しましょう。

 上記テストコードは、最初に設定した文字列と、PubsRepositoryオブジェクトのSendMessageメソッドを実行した戻り値を比較しアサートを実行しています。

 続いて、リポジトリパターンを適用しているPubsControllerクラスのテストも実施します。今回は新たにデータを作成するCreateメソッドのテストを紹介します。

 最初にテストコードを確認してみましょう。

HomeControllerクラス上のテストメソッド
[TestMethod()]
public void CreatePostTest()
{
    // 準備 理想値やテストデータや値や各コントローラーの生成
    int expected = 11;
    // 10件のテストデータを作成
    var testData = FakePubsData.CreateTestData();
    var repository = new PubsRepositoryTest(testData);
    var controller = new PubsController(repository);
    // 11件目のデータの用意
    titles SampleTitles = new titles()
    {
        title_id = "CreateTest",
        advance = 1000,
        title = "Sample Book Create",
        royalty = 10,
        price = 30,
        pubdate = DateTime.Now.AddDays(1),
        notes = "追加!",
        type = "Fake",
    };

    // 実行 11件目のデータを作成
    var actual = controller.Create(SampleTitles);

    // アサート
    Assert.AreEqual(expected, repository.FindAllTitles().Count());
    Assert.IsInstanceOfType(actual, typeof(RedirectToRouteResult));
}

 テストコード自体は特別なことはしていません。上記テストコードのポイントはダミーオブジェクト差し替えの部分です。

 CreatePostTestメソッドでは、Createメソッドに値が保存されるかをテストするために正常に動作した場合の理想値11を設定し、その後ダミーデータを作成するFakePubsDataクラスのCreateTestDataメソッドを利用し、10件のデータを生成します。続いて11件目のデータを用意し、Createメソッド実行後にデータが11件に増えているかどうかをアサートでチェックしています。

コントローラーの単体テスト

 最後にHomeControllerクラスとPubsControllerクラスそれぞれ単体テストを記載します。

 HomeControllerクラスのIndexアクションメソッドにDIを利用してPubsRepositoryオブジェクトのSendMessageメソッドを実装しています。SendMessageメソッドの戻り値はViewBag.SendMessageに対して設定しているので、ViewBagの値に対してテストを実施してみます。

HomeControllerクラス上のテストメソッド
[TestMethod]
public void Index()
{
    //// 準備 理想値の設定
    string expected = String.Format("現在の日時は{0}です。これはDIの実装例です。", System.DateTime.Now.ToString());
    string actual; 
    
    var TestData = FakePubsData.CreateTestData();
    PubsRepositoryTest repository = new PubsRepositoryTest(TestData);
    ISendMessage im = repository;
    HomeController controller = new HomeController(im);
    
    // 実行 Indexアクションメソッドの呼び出しと実行結果の設定
    ViewResult result = controller.Index() as ViewResult;
    actual = result.ViewBag.SendMessage;

    // アサート
    Assert.AreEqual(expected, actual);
}

 基本的にPubsRepositoryオブジェクトのSendMessageメソッドのテストコードと同じです。違いはViewBag.SendMessageオブジェクトの値をアサート対象としているかどうかになります。単体テストは可能な限りアサートの範囲や数を絞ることを心がけると良いでしょう。上記テストコードは他に変数resultがnullかどうかアサートするなどの記述もできます。ただ、フォーカスを絞ることで、より可読性が向上し、メインロジックもすっきりしたものになるでしょう。もちろんこれはケースバイケースなのでURLルーティングのテストで記載したように確かめたいことに対して複数のアサートを記述するなども実際には多分にあります。

 なお、コントローラーはHttpContextに依存してしまうことがあります。認証や、ViewBag/ViewDataの利用、Session情報を知りたい場合はこれらのモックを作成する必要もあります。

 基本的に利用方法は前回解説したモックオブジェクトの生成方法と変わりありません。

ControllerContextのモックオブジェクトの作成例
// Controllerのモックオブジェクト生成例
Mock<ControllerContext> mock = new Mock<ControllerContext>();
mock.SetupGet(p => p.HttpContext.User.Identity.Name).Returns("Naokio");
mock.SetupGet(p => p.HttpContext.Request.IsAuthenticated).Returns(true);

 上記では、ユーザー認証のフィルターが正確に動作しているかどうかテストしたい場合に記述するコードです。Naokioというユーザー名を取得できるモックオブジェクトを生成し、リクエストが認証されているかBool型で示すIsAuthenticatedプロパティの値をTrueに設定しています。

 なお、コントローラーでHttpContextを扱うテストを実施したい場合はURLルーティングのテスト同様にHttpContextBaseクラスのモックオブジェクトを生成しテストすると良いでしょう。

まとめ

 今回はMVC 3のサンプルプロジェクトに対する単体テストの記述方法を単体テストを利用する上でのTipsを織り交ぜながら紹介しました。サンプルプロジェクトはシンプルなため、テストコードもシンプルになりましたが、実際の業務アプリケーションではより複雑なテストメソッドになるかと思います。

 今回紹介した内容を積み重ねていくことで、テストの自動化やテストファーストとなるTDDなどの開発手法にも発展させることができます。先ずは基本となる単体テストの記述を継続して行うようにしていきましょう!

 一年に渡り不定期で連載してきたASP.NET MVC 3の連載も今回で最終回です。すでにASP.NET MVC 4のBetaも公開されていますが、ASP.NET MVC自体は3の段階で開発手法やフレームワークとしての大枠は完成したと言える状態です。

ASP.NET MVC 4の新機能

 ASP.NET MVC 4では以下のような機能が新たに追加されています。

 

  • 新たなスクリプトファイルの追加

    スマートデバイス対応のスクリプトライブラリjQuery MobileやJavaScriptでMVVMパターンを実現するknockout.jsなどの追加

  • ASP.NET Web API

    RESTfulなWebサービス構築を実施するHTTPサービス構築用のフレームワーク

  • Simple Page Application

    ASP.NET Web APIと複数のJavaScriptライブラリを駆使し、単一のページでCRUD操作すべてを実現できるWebアプリケーションを開発するフレームワークとテンプレート

  • Display Mode

    複数のViewを用意し、リクエストのあったデバイスやブラウザに応じてViewを切り替える機能

 他にもさまざまな機能が追加されています。ASP.NET MVC 4自身はモバイル対応やRESTfulなWebサービス構築をサポートするASP.NET Web APIテンプレートなど、ASP.NET MVCというより、MVC周りで活用できる技術やMVCの機能を活かして作れる簡単なアプリケーションテンプレートなどの機能強化がメインです。ASP.NET MVC 3で得た知識はASP.NET MVC 4でも活かせます。

 また、ASP.NET MVCを活用したサンプルなども増えてきているので、さまざまなソースやテストコードを見て学習してみるとよいでしょう。

参考文献

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
ASP.NET MVC3入門連載記事一覧

もっと読む

この記事の著者

山田 祥寛(ヤマダ ヨシヒロ)

静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for Visual Studio and Development Technologies。執筆コミュニティ「WINGSプロジェクト」代表。主な著書に「独習シリーズ(Java・C#・Python・PHP・Ruby・JSP&サーブレットなど)」「速習シリーズ(ASP.NET Core・Vue.js・React・TypeScript・ECMAScript、Laravelなど)」「改訂3版JavaScript本格入門」「これからはじめるReact実践入門」「はじめてのAndroidアプリ開発 Kotlin編 」他、著書多数

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

WINGSプロジェクト ナオキ(ナオキ)

WINGSプロジェクトについて>有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。2018年11月時点での登録メンバは55名で、現在も執筆メンバを募集中。興味のある方は、どしどし応募頂きたい。著書記事多数。 RSS X: @WingsPro_info(公式)、@WingsPro_info/wings(メンバーリスト) Facebook

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/6543 2012/05/09 14:00

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング