概要
ASP.NET MVCの単体テスト(MsTest)を利用したいが、テスト対象となるControllerクラスで以下の処理を行っていたのでそれに対応するテストクラスを作った。
【Controllerクラスでの特記すべき処理】
- Sessionを利用している。
- ユーザー情報を利用しており、"HttpContext.User.Identity.GetUserId()"でUserIdを使っている。
- Entity FrameworkのProviderを利用している。
この辺の情報は英語の情報しかなく、結構苦労しました。
ただ "ASP.NET MVC unit test '調べたいこと'" みたいなキーワードで検索すると、英語ですが有益な情報が得られました。
対応策
全体的な作りとしてMoqを使っています。
Sessionを利用する。
テスト対象のコードを実行する前に、テストコード側で以下の処理を行います。この処理をしないとSessionを使う箇所でNullReferenceExceptionが発生し、異常終了します。
// Sessionを生成し、ControllerContextにセットする。
var session = new Mock<HttpSessionStateBase>();
controllerContext.Setup(p => p.HttpContext.Session).Returns(session.Object);
ユーザー情報を利用する。
さらに"HttpContext.User.Identity.GetUserId()"を利用します。
テスト対象のコードを実行する前に、テストコード側で以下の処理を行います。
以下のソースでポイントとなるのはClaimをセットする点です。これがなければGetUserId()は使えません。
// Identityを生成し、GetUserID()が出来る様にClaimをセットする。
var identity = new GenericIdentity(username);
List<Claim> claims = new List<Claim>{
new Claim("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name", username),
new Claim("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier", userid)
};
identity.AddClaims(claims);
// Principalを生成し、HttpContextのUserにセットする。
var principal = new GenericPrincipal(identity, null);
controllerContext.Setup(p => p.HttpContext.User).Returns(principal);
Entity FrameworkのProviderを利用する。
テスト対象のコードを実行する前に、テストコード側で以下の処理を行います。
// 未使用DLLはテストプロジェクトの実行領域にコピーされないためインスタンスを生成する。
var instance = System.Data.Entity.SqlServer.SqlProviderServices.Instance;
ソースコード全体
テストクラスの全体像は以下の通りです。TestInitialize()に記述した内容は基底クラスに持って行くべきなのであくまで参考まで。
using Moq;
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using TestWeb.Controllers;
using System.Web.Mvc;
using TestWeb.Models.Views;
using System.Web;
using System.Security.Principal;
using System.Security.Claims;
namespace TestWeb.Tests.Controllers
{
/// <summary>
/// TestCardsControllerTest の概要の説明
/// </summary>
[TestClass]
public class TestCardsControllerTest
{
private TestCardsController ctrl;
/* テストの初期化処理毎。テスト毎に実行される */
[TestInitialize]
public void TestInitialize()
{
// テスト対象となるユーザーを設定
string username = "XXXX";
string userid = "340F9405-4A1F-4599-A8F6-...........";
// ControllerContextを生成
var controllerContext = new Mock<ControllerContext>();
// Sessionを生成し、ControllerContextにセットする。
var session = new Mock<HttpSessionStateBase>();
controllerContext.Setup(p => p.HttpContext.Session).Returns(session.Object);
// Identityを生成し、GetUserID()が出来る様にClaimをセットする。
var identity = new GenericIdentity(username);
List<Claim> claims = new List<Claim>{
new Claim("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name", username),
new Claim("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier", userid)
};
identity.AddClaims(claims);
// Principalを生成し、HttpContextのUserにセットする。
var principal = new GenericPrincipal(identity, null);
controllerContext.Setup(p => p.HttpContext.User).Returns(principal);
// テスト対象のコントローラを生成し、ControllerContextをセットする。
ctrl = new TestCardsController();
ctrl.ControllerContext = controllerContext.Object;
// 未使用DLLはテストプロジェクトの実行領域にコピーされないためインスタンスを生成する。
var instance = System.Data.Entity.SqlServer.SqlProviderServices.Instance;
}
[TestMethod]
public void IndexTest()
{
var result = ctrl.Index() as ViewResult;
Assert.IsNotNull(result);
}
[TestMethod]
public void SearchTest()
{
var result = ctrl.Search(new TestCardViewModel()) as ViewResult;
Assert.IsNotNull(result);
}
}
}
これで、ASP.NET MVCで単体テストが楽しめます。
参考URL
http://stackoverflow.com/questions/19006624/how-to-mock-httpcontext-user-identity-name-in-asp-net-mvc-4
http://stackoverflow.com/questions/14033193/entity-framework-provider-type-could-not-be-loaded
http://stackoverflow.com/questions/14033193/entity-framework-provider-type-could-not-be-loaded
http://qiita.com/sugasaki/items/b6189a423cb7a5d1c2a9