Skip to main content

Visual Studio 2012 自習書

第 4 回
Web アプリケーションのテスト (前編)

更新日: 2013 年 6 月 26 日

※ 本自習書の PDF ファイルは 後編よりダウンロードいただけます。


目次

  1. Web アプリケーションのテスト
  2. MVC パターンの理解と事前準備
  3. Web における単体テスト

1. Web アプリケーションのテスト

Web アプリケーションは企業における様々な活動において、ユーザーあるいはパートナー企業と直接的にコンタクトを持つことを可能にするシステムです。そのため Web アプリケーションによるユーザー体験 (User Experience) は、直接的にサービスや企業のイメージに結び付きやすい傾向にあります。つまり、高い品質のユーザー体験を提供できればサービスや企業の印象もよくなりますし、その逆も成り立ってしまうということです。

従って Web アプリケーションの開発においてはアプリケーションの品質を十分に確認し、また誤ったデータ操作や顧客対応が行われないようにテストを行うことが重要になります。Web アプリケーションは通常は 3 階層 (ブラウザー、アプリケーション サーバー、データベース サーバー) 以上に分割され、また他のシステムやサービスと連携を行うことがほとんどです。そのためテストを用意する場合には、それらの階層関係を考慮しつつテストの目的を明確にしたうえでテストを作成する必要があります。

Visual Studio では主に以下のようなテスト シナリオにおいて、ツールの支援を利用したテストの作成、実施を行うことが可能になります。

図 1: Visual Studio で可能なテストのパターン - [1] 手動テスト、[2] 単体テスト、[3] Web テスト、[4] 負荷テスト、[5] コード化された UI テスト
図 1

今回の自習書では、上記のパターンのうち、[2] の単体テスト、[3] の Web テスト (正確には "Web パフォーマンス テスト" という名称)、および [4] の負荷テストの概要を体験いただきます。

Visual Studio 2012 試用版の入手方法

Visual Studio 2012 の試用版は無償で以下の Web サイトよりダウンロード入手いただけます。

(本自習書の内容をお試しいただくには Ultimate の環境をご用意ください)

ページのトップへ


2. MVC パターンの理解と事前準備

ハンズオンの準備として、ここでは Visual Studio 2012 による MVC フレームワークの概要理解と、テスト用アプリケーションの準備を行います。

  1. 最初に、テスト対象とするアプリケーションを用意します。Visual Studio で新規プロジェクト作成 (メニューから「ファイル」→「新規作成」→「プロジェクト」を選択) を行い、表示されたウィザードにおいて、"Web" のプロジェクト テンプレートのなかから "ASP.NET MVC 4 Web アプリケーション" を選択し、プロジェクトの名称として "SampleWeb" を入力し、「OK」をクリックします。

    図 2.1: [新しいプロジェクト] ダイアログ ボックス
    図 2.1

  2. 引き続き MVC4 に含まれるテンプレートが表示されますので "インターネット アプリケーション" のテンプレートを選択し、"単体テスト プロジェクトを作成する" のチェック ボックスを選択したうえで、「OK」をクリックします (ビュー エンジンは "Razor" を使用)。

    図 2.2 [新しい ASP.NET MVC 4 プロジェクト] ダイアログ ボックス
    図 2.2

  3. プロジェクト情報が作成され、"SampelWeb" というソリューションの下に、"SampleWeb" プロジェクトと "SampleWeb.Test" プロジェクトが作成されます。

    図 2.3.1 ソリューション エクスプローラー
    図 2.3.1

    いくつかのフォルダー、ファイルがありますが、MVC テンプレートを使用する際に最も重要なのは、以下の画面ショットで赤い囲みで囲っているコントローラー (Controllers フォルダーの内容)、モデル (Models フォルダーの内容)、およびビュー (Views フォルダーの内容) です。

    図 2.3.2: [ソリューション エクスプローラー] SampleWeb プロジェクト - Controllers フォルダーの内容: AccountController.cs, HomeController.cs、Models フォルダーの内容: AccountModels.cs、Views フォルダーの内容: Account フォルダー, Home フォルダー、Shared フォルダー, _ViewStart.cshtml, Web.config
    図 2.3.2

    ASP.NET MVC フレームワークは、これらの頭文字をとった MVC デザイン パターン (Model, View, Controller) をもとにしたテンプレートです。

    簡単にそれぞれの役割を記述すると以下のようになります。

    Model: モデル

    ソフトウェアのロジックの中心となるオブジェクトを集めたもの。MVC パターンでの Model はでは広義の「モデル」を取扱い、純粋なエンティティ オブジェクトだけでなく、エンティティ オブジェクトに対する制御を行うオブジェクトも含みます。

    以下は、MVC プロジェクトにおいて Views フォルダーの下にある AccountModels.cs に含まれる LocalPasswordModel のコードです。

    図 2.3.3: LocalPasswordModel のコード
    図 2.3.3

    LocalPasswordModel クラスは、MVC プロジェクトにおける下回りの機能 (認証関連の機能) を提供するための Model であるため、単純な構造となっていますが、実際に MVC にて開発を進める場合には Model として様々なクラス (オブジェクト) を追加し、ソフトウェアのロジックを作りこむことになります。

    View: ビュー

    ソフトウェアにおける外観を提供するオブジェクトを集めたもの。今回の MVC テンプレートでは ビュー エンジンとして "Razor" (レイザー) 記法を利用しています。Razor 記法においては、「@」で開始するキーワードを利用して、HTML の中にコードを埋め込むような形で View を記述します。下記は今回のテンプレートで作成された index.cshtml ファイルの内容です。

    図 2.3.4: index.cshtml ファイル内容
    図 2.3.4

    他方、プロジェクト作成時にビュー エンジンとして (これまで同様の) ASPX を使用すると "asp" で始まる独自のタグ (例えば <asp:Content> タグ) を使用し、View を記述します (下記は、今回作成した SampleWeb とは別に ASPX のビュー エンジンを使用した MVC 4 プロジェクトを作成した内容です)。

    図 2.3.5: MVC 4 プロジェクト作成内容
    図 2.3.5

    MVC のデザイン パターンは、Model と View と Controller の相互の依存度を下げるためのパターンで、特に View に関しては Model および Controller からの依存度が低くなうようなパターンとなるため、View として Razor と ASPX のいずれのエンジンを使用する場合でも Model や Controller に関しては基本的に共通のロジックで記述することができるようになっています。

    Controller: コントローラー

    MVC における Controller はソフトウェアと外部のやり取りについて責務を持つオブジェクトです。例えば今回の SampleWeb のように MVC テンプレートにおいて、「インターネット アプリケーション」のテンプレートを選択した際には、URL の一部やフォーム ポスト パラメーターとして渡された情報 (引数) を Model に対し渡すことで、適切なビジネス ロジックの実行を促します。また、Model によって行われたロジックに基づき、その結果を適切な View に渡す役割も持っています (それを「どのように表示するか」は View の責務になります)。

    例えば、MVC テンプレートにおいては「Web API」というテンプレートもありますが、この "Web API" では Model のロジック実行結果を Controller 経由で View に返し表示を行うのではなく、Controller から HTTP Response として返す作りになっています。

    図 2.3.6: プロジェクト テンプレート
    図 2.3.6

    しかし、外部から URL の一部あるいはフォーム ポスト パラメーターとして渡された情報を Model に引き渡す、そして Model が実行したロジックの結果を外部に返す、という点では同じ責務を持っているといえます。

    本自習書冒頭の解説においてテストの分類を行った図を利用して、これら MVC が対応するレイヤーと Visual Studio のテスト機能が対象とするレイヤーをまとめると以下のようになります。

    図 2.3.7: MVC と Visual Studio テスト機能の対応図
    図 2.3.7

  4. 引き続き、この後のテストにて使用するための準備を行います。Visual Studio で、F5 を押し (もしくはメニューから、「デバッグ」→「デバッグの開始」を選択) デバッグを開始します。ローカル PC 上で Web アプリケーションがホストされ、以下のような Web が表示されます。

    図 2.4: デバック開始時に表示される Web
    図 2.4

  5. 画面右上にある「登録」のリンクをクリックし、ユーザー登録画面で、ユーザー名 "admin1"、パスワード "Password1"、パスワードの確認入力 "Password1" を入力し、「登録」を行います。

    図 2.5: 登録: 新しいアカウントを作成します
    図 2.5

  6. 登録が成功したら、画面右上の「ログオフ」を押し、ユーザー admin1 でのログイン状態を解除し、同様に "admin2" (パスワードは "Password2")、および "admin3" (同じく "Password3") のユーザー登録を行います。これらが完了したら、Web アプリケーションのデバッグを終了してください。
  7. "デスクトップ" や "ドキュメント" などのわかりやすいフォルダーにて、Data.csv というファイルを作成し、以下のように CSV 形式でデータを入力し、保存してください。

    図 2.7: CSV 形式でデータ保存 - User,Pss/admin1,Pasword1/admin2,Pasword2/admin3,Pasword3
    図 2.7

以上で準備は完了です。

ページのトップへ


3. Web における単体テスト

1 つ目のハンズオンでは、Visual Studio を使った Web アプリケーションへの単体テストについて学びます。再度整理ですが、MVC パターンにおけるレイヤーと、Visual Studio が提供する単体テスト機能の守備範囲は以下のように分類できます。

図 3.1: [2] 単体テスト
図 3.1

つまり上記のように、(1) Model に対する単体テスト、ならびに (2) Controller に対する単体テスト、として単体テストの実施が可能です。

このうち、(1) の Model に対する単体テスト、については基本的に Web 特有の事項はありません。ソフトウェにおけるロジック全般を担当する Model に対する単体テストの作り込に関しては、本自習書シリーズの初回 「Visual Studio 2012 による単体テスト」などを参考にテストをいただくのがよいかと思います。
今回のハンズオンでは、(2) の Controller に対する単体テスト、について確認したいと思います。

  1. 今回の自習書では、プロジェクト作成時に単体テストも合わせて作成を行っています。ソリューション エクスプローラーにて、"SampleWeb.Test" プロジェクトを確認すると、"Controllers" フォルダー配下に "HomeController" クラスに対するテストを行う "HomeControllerTest" クラスとそのメソッドが確認できます。

    図 3.2: ソリューション エクスプローラー
    図 3.2

  2. 実際にテスト対象となる "HomeController" クラスの "Index" メソッド、および "HomeControllerTest" クラスにおける "Index" メソッドを比較してみましょう。

    図 3.3: "Index" メソッドの比較
    図 3.3

    テスト クラスである "HomeControllerTest" では、"HomeController" をインスタンス化し、"Index" メソッドを呼び出し、戻り値である ActionResult を取得しています (テスト側ではこれを ViewResult クラスにキャストしています)。

    Assert 文としては、「"HomeController" クラスの "Index" を呼び出したのち、ViewBag のメッセージには "Modify this..." という文字列が入っている」という内容で設定し、1 つの単体テストとしています。

    MVC アプリケーションにおいて Controller に対する単体テストを実施する場合は、プロジェクト作成時に "単体テスト プロジェクト" を合わせて作成しておく (事前準備の手順 2 ) ことで、このようなひな形が用意されていますので、これをカスタマイズして単体テストを作りこむことが可能です。

    さて、"SampelWeb" プロジェクトにおいては "HomeController" の他に "AccountController" も存在しています。しかしながら、テスト プロジェクトである "SampleWeb.Test" プロジェクトには "AccountController" のテスト用コードがありません。

    実際のところ、Web アプリケーションの Controller 部分の振る舞いは、他のコンポーネントや MVC Framework の機能に依存している箇所が多く、テスト容易性の観点から考えると、"HomeCotroller" のように単純なテスト パターンしか出ない Controller はむしろ少数派になります。Post メソッドが多く、また他のコンポーネントへの依存度の高い "AccountController" のテスト クラスがデフォルトで存在しないのはそのような理由もあるかと思います。

  3. デフォルトでは存在しない "AccountController" のテスト クラスですが、今回の "SampleWeb.Test" プロジェクトにおいて作成を行ってみましょう。ソリューション エクスプローラーにおいて、"SampleWeb.Test" プロジェクトの "Controllers" フォルダーにて右クリックを行い、表示されたメニューから「追加」→「単体テスト」を選択します。

    図 3.4: テスト クラス作成手順
    図 3.4

  4. 作成されたファイルの名前を "AccountControllerTest.cs" に変更してください。

    図 3.5: ファイルの名前変更
    図 3.5

  5. "AccountControllerTest" クラスを開き、テスト メソッドの名称を変更しましょう。"TestMethod1" を選択した状態で右クリックを押し、表示されたメニューから 「リファクター」→「名前の変更」を選びます。

    図 3.6: テスト メソッド名称変更手順
    図 3.6

  6. 新しい名前として "Login 失敗時のテスト" と入力し、「OK」を押してください。引き続き表示されるプレビュー内容を確認し、「適用」を押します。

    図 3.7: [名前の変更] ダイアログ ボックス
    図 3.7

  7. 次に、ソリューション エクスプローラーにて、"SamapleWeb.Test" プロジェクトの参照設定に、同一ソリューション内の "SampleWeb" を追加します ("SampelWeb.Test" の "参照設定" を右クリックして出てくるメニューにて、"参照の追加" を選択すると下記の画面ショットのようなウィザードが表示されるので、そこで追加を行います)。

    図 3.8: [参照マネージャー] ダイアログ ボックス
    図 3.8

  8. 同様に今回のテストにて使用する WebMatrix.WebData アセンブリを追加します。"参照の追加" で表示されるウィザードにおいて、"アセンブリ" を選択し、検索ボックスに "webmatrix" と入力します。いくつかのアセンブリが表示されますが、このうち "WebMatrix.WebData" のバージョン "2.0.0.0" を選択し、「OK」を押します。

    図 3.9: アセンブリ追加図 3.9

  9. "SampleWeb.Test" プロジェクトの "参照設定" の項目に追加され、表示された "WebMatrix.WebData" アセンブリを右クリックし、表示されたメニューから "Fakes アセンブリに追加" を選択します。

    図 3.10: "Fakes アセンブリに追加" を選択
    図 3.10

  10. AccoountControllerTest を以下のように変更します。

    using System;
    using Microsoft.VisualStudio.TestTools.UnitTesting;

    using System.Web.Mvc;
    using SampleWeb;
    using SampleWeb.Models;
    using SampleWeb.Controllers;
    using Microsoft.QualityTools.Testing.Fakes;

    namespace SampleWeb.Tests.Controllers
    {
        [TestClass]
        public class AccountControllerTest
        {
            [TestMethod]
            public void Login失敗時のテスト()
            {
                AccountController controller = new AccountController();
                LoginModel model = new LoginModel();
                ViewResult result;

                // (1) 1回目の Assert
                Assert.AreEqual(0, controller.ModelState.Count);

                // (2) Fake による関連コンポーネントの動作の偽装
                using (ShimsContext.Create())
                {
                    WebMatrix.WebData.Fakes.ShimWebSecurity.LoginStringStringBoolean
                        = (str1, str2, bool1) => false;
                    result = controller.Login(model, "") as ViewResult;
                }

                // (3) 2回目の Assert
                Assert.AreEqual(1, controller.ModelState.Count);
            }
        }
    }

    このテスト メソッドでは、(1) の 1 回目の Assert において、AccountController オブジェクトの ModelState においてエラーがない状態であることを確認しています (ModelState に情報がないことを、Count プロパティの値が 0 であることを確認することで確かめています)。
    また、(2) のコードにより、AccountController クラスの Login(LoginModel, String) のメソッドにおいて、ログイン情報を確認する WebSecurity クラスの Login(String, String, Boolean) メソッドが必ず失敗するように設定を行っています。ここで、WebSecurity クラスの Login メソッドに "登録された正しいユーザー情報が渡された際に True が返ること"、あるいは "登録されたユーザー情報に一致しないユーザー名やパスワードが渡った際には False が返ること"、という確認は「AccountController クラスの Login メソッドの単体テスト」の対象外になることに注意してください。これは AccountController の Login メソッドとしては、「引数として受け取ったユーザー情報を WebSecurity クラスに引き渡し、その結果をもとに画面遷移 (次の View の表示) を行ったり、画面 (元のログイン画面の View) にエラー メッセージを渡す」ということを責務にしているためです (シナリオ テスト、あるいは結合テストとして、「ログイン画面で正しいユーザー情報が入力された場合に、ログインに成功し次の画面に遷移する」といったテストを用意することはあります。)

    さて、(2) の Fake を使ったコードより、この後に AccountController の Login(LoginModel, String) メソッドが呼ばれた際に、ログイン情報の検証に失敗した (ユーザー名と ID の情報が存在していない) 場合の、AccountController の振る舞いをテストできることになります。

    図 3.11: 認証失敗時のロジック
    図 3.11

  11. ソリューションのビルドを行うと、テスト エクスプローラーに以下のように今回作成した "Login 失敗時のテスト" のメソッドが表示されます (テスト エクスプローラーが表示されない場合、Visual Studio のトップ メニューの「テスト」→「ウィンドウ」→「テスト エクスプローラー」と選択ください)。

    図 3.12: テスト エクスプローラー
    図 3.12

  12. テスト エクスプローラーにおける "Login 失敗時のテスト" を右クリックし、表示されたメニューから「選択したテストの実行」を選択し、テストを実行してください。

    図 3.13: 「選択したテストの実行」を選択
    図 3.13

  13. テストが成功し、以下のように表示されます。これにより 10 の画面ショットにあった (3) の Assert にも成功した (ModelState にエラーが 1 つ追加された) ことが確認できます。

    図 3.14: テスト エクスプローラー
    図 3.14

    今回の "Login 失敗時のテスト" では、他のコンポーネントとの依存性がそれほど多くないパターン (WebSecurity の偽装のみで動作するパターン) でしたが、Controller はソフトウェアと外部のやり取りについて責務を持つオブジェクトであるため、通常はこれ以外にも様々なコンポーネントとの関連を持っています。
    これらを勘案しつつ Controller の単体テストを作成することは場合によってはコストがかかることもあります。

    Controller に対する単体テストの実装が難しい場合や、関連するコンポーネントの動作も合わせてテストしたいような場合、次の章で紹介する "Web パフォーマンス テスト" を利用してテストを行う、という選択肢もあり得ます。

ここでは以上で 1 つ目のハンズオンを終了します。

1 つ目のハンズオンでは、Visual Studio を使った Web アプリケーションへの単体テストについて学びました。

  1. (1) 単体テストの分類 (Model への単体テストと、Controller への単体テスト)
  2. (2) Controller に対する単体テスト
    1. (ア) HomeControllerTest の内容の確認
    2. (イ) AccountControllerTest の作成と Fake を使った依存性の排除


Visual Studio 2012 自習書
シリーズ

  1. Visual Studio 2012 による単体テスト
  2. Visual Stusio 2012 を使ったコード分析
  3. Team Foundation Server のインストールと最初の一歩
  4. Web アプリケーションのテスト (前編)
  5. Web アプリケーションのテスト (後編)

一覧を見る >>

自習書 (Do-It-Yourself シリーズ)

一覧を見る >>

ページのトップへ