ASP.NET MVC アプリケーションの単体テストを作成する (C#)

作成者: Stephen Walther

コントローラー アクションの単体テストを作成する方法について説明します。 このチュートリアルでは、コントローラー アクションによって特定のビューが返されるか、特定のデータ セットが返されるか、または別の種類のアクション結果が返されるかをテストする方法について、Stephen Walther が説明します。

このチュートリアルの目的は、ASP.NET MVC アプリケーションでコントローラーの単体テストを記述する方法を示すことです。 ここでは、3 種類の単体テストをビルドする方法について説明します。 コントローラー アクションによって返されるビューをテストする方法、コントローラー アクションによって返されるビュー データをテストする方法、1 つのコントローラー アクションによって 2 つ目のコントローラー アクションにリダイレクトされるかどうかをテストする方法について説明します。

テスト用のコントローラーの作成

まず、テスト用のコントローラーを作成します。 ProductController という名前のコントローラーはリスト 1 に含まれています。

リスト 1 – ProductController.cs

using System;
using System.Web.Mvc;

namespace Store.Controllers
{
     public class ProductController : Controller
     {
          public ActionResult Index()
          {
               // Add action logic here
               throw new NotImplementedException();
          }

          public ActionResult Details(int Id)
          {

               return View("Details");
          }
     }
}

ProductController には、Index()Details()という名前の 2 つのアクション メソッドが含まれています。 どちらのアクション メソッドもビューを返します。 Details() アクションは Id という名前のパラメーターを受け入れることに注意してください。

コントローラーによって返されるビューのテスト

ProductController が正しいビューを返すかどうかをテストするとします。 ProductController.Details() アクションが呼び出されたときに、[詳細] ビューが返されることを確認します。 リスト 2 のテスト クラスには、ProductController.Details() アクションによって返されるビューをテストするための単体テストが含まれています。

リスト 2 – ProductControllerTest.cs

using System.Web.Mvc;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Store.Controllers;

namespace StoreTests.Controllers
{
     [TestClass]
     public class ProductControllerTest
     {
          [TestMethod]
          public void TestDetailsView()
          {
               var controller = new ProductController();
               var result = controller.Details(2) as ViewResult;
               Assert.AreEqual("Details", result.ViewName);

          }
     }
}

リスト 2 のクラスには、TestDetailsView() という名前のテスト メソッドが含まれています。 このメソッドには、3 行のコードが含まれています。 1 行目のコードは、ProductController クラスの新しいインスタンスを作成します。 2 行目のコードは、コントローラーの Details() アクション メソッドを呼び出します。 最後に、最後の行のコードは、Details() アクションによって返されるビューが詳細ビューであるかどうかを確認します。

ViewResult.ViewName プロパティは、コントローラーによって返されるビューの名前を表します。 このプロパティのテストに関する 1 つの大きな警告です。 コントローラーがビューを返すことができる方法は 2 つあります。 コントローラーは、次のようなビューを明示的に返すことができます。

public ActionResult Details(int Id)
{
     return View("Details");
}

または、次のように、コントローラー アクションの名前からビューの名前を推論できます。

public ActionResult Details(int Id)
{
     return View();
}

このコントローラー アクションでは、Details という名前のビューも返されます。 ただし、ビューの名前はアクション名から推論されます。 ビュー名をテストする場合は、コントローラー アクションからビュー名を明示的に返す必要があります。

リスト 2 で単体テストを実行するには、キーボードの組み合わせ Ctrl + R、A を入力するか、[ソリューションですべてのテストを実行する] ボタンをクリックします (図 1 を参照)。 テストに合格すると、図 2 の [テスト結果] ウィンドウが表示されます。

Run All Tests in Solution

図 01: ソリューションですべてのテストを実行する (クリックするとフルサイズの画像が表示されます)

Success!

図 02: 成功! (クリックするとフルサイズの画像が表示されます)

コントローラーによって返されるビュー データのテスト

MVC コントローラーは、View Data と呼ばれるものを使用してビューにデータを渡します。 たとえば、ProductController Details() アクションを呼び出すときに特定の製品の詳細を表示するとします。 その場合は、(モデルで定義されている) Product クラスのインスタンスを作成し、View Data を利用してインスタンスを Details ビューに渡すことができます。

リスト 3 で変更された ProductController には、Product を返す更新された Details() アクションが含まれています。

リスト 3 – ProductController.cs

using System;
using System.Web.Mvc;

namespace Store.Controllers
{
     public class ProductController : Controller
     {
          public ActionResult Index()
          {
               // Add action logic here
               throw new NotImplementedException();
          }

          public ActionResult Details(int Id)
          {
               var product = new Product(Id, "Laptop");
               return View("Details", product);
          }
     }
}

まず、Details() アクションによって、ラップトップ コンピューターを表す Product クラスの新しいインスタンスが作成されます。 次に、Product クラスのインスタンスが 2 番目のパラメーターとして View() メソッドに渡されます。

単体テストを記述して、期待されるデータがビュー データに含まれているかどうかをテストできます。 リスト 4 の単体テストでは、ProductController Details() アクション メソッドを呼び出すときに、ラップトップ コンピューターを表す Product が返されるかどうかをテストします。

リスト 4 – ProductControllerTest.cs

using System.Web.Mvc;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Store.Controllers;

namespace StoreTests.Controllers
{
     [TestClass]
     public class ProductControllerTest
     {

          [TestMethod]
          public void TestDetailsViewData()
          {
               var controller = new ProductController();
               var result = controller.Details(2) as ViewResult;
               var product = (Product) result.ViewData.Model;
               Assert.AreEqual("Laptop", product.Name);
          }
     }
}

リスト 4 では、TestDetailsView() メソッドは Details() メソッドを呼び出して返されたビュー データをテストします。 ViewData は、Details() メソッドを呼び出すことによって返される ViewResult のプロパティとして公開されます。 ViewData.Model プロパティには、ビューに渡された製品が含まれています。 このテストでは、ビュー データに含まれる製品に Laptop という名前があることを確認するだけです。

コントローラーによって返されるアクション結果のテスト

より複雑なコントローラー アクションでは、コントローラー アクションに渡されるパラメーターの値に応じて、さまざまな種類のアクション結果が返される場合があります。 コントローラー アクションは、ViewResultRedirectToRouteResultJsonResult など、さまざまな種類のアクションの結果を返すことができます。

たとえば、リスト 5 の変更された Details() アクションは、有効な製品 ID をアクションに渡すと Details ビューを返します。 無効な製品 ID (値が 1 未満の ID) を渡すと、Index() アクションにリダイレクトされます。

リスト 5 – ProductController.cs

using System;
using System.Web.Mvc;
namespace Store.Controllers
{
     public class ProductController : Controller
     {
          public ActionResult Index()
          {
               // Add action logic here
               throw new NotImplementedException();
          }
          public ActionResult Details(int Id)
          {
               if (Id < 1)
                    return RedirectToAction("Index");
               var product = new Product(Id, "Laptop");
               return View("Details", product);

          }
     }
}

リスト 6 の単体テストを使用して、Details() アクションの動作をテストできます。 リスト 6 の単体テストでは、値が -1 の ID が Details() メソッドに渡されたときに、Index ビューにリダイレクトされることを確認します。

リスト 6 – ProductControllerTest.cs

using System.Web.Mvc;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Store.Controllers;
namespace StoreTests.Controllers
{
     [TestClass]
     public class ProductControllerTest
     {
          [TestMethod]
          public void TestDetailsRedirect()
          {
               var controller = new ProductController();
               var result = (RedirectToRouteResult) controller.Details(-1);
               Assert.AreEqual("Index", result.Values["action"]);

          }
     }
}

コントローラー アクションで RedirectToAction() メソッドを呼び出すと、コントローラー アクションは RedirectToRouteResult を返します。 テストでは、RedirectToRouteResultIndex という名前のコントローラー アクションにユーザーをリダイレクトするかどうかを確認します。

まとめ

このチュートリアルでは、MVC コントローラー アクションの単体テストをビルドする方法について説明しました。 最初に、コントローラー アクションによって適切なビューが返されるかどうかを確認する方法を学習しました。 ViewResult.ViewName プロパティを使用してビューの名前を確認する方法を学習しました。

次に、View Data の内容をテストする方法を説明しました。 コントローラー アクションを呼び出した後、View Data で適切な製品が返されたかどうかを確認する方法について学習しました。

最後に、コントローラー アクションからさまざまな種類のアクション結果が返されるかどうかをテストする方法について説明しました。 コントローラーが ViewResult または RedirectToRouteResult を返すかどうかをテストする方法について学習しました。