ユニットテストでセッションを使用する
前提
MVC(.net Framework)を使用したWebアプリケーションで、セッションのテストを行いたい。または、セッションを使用したいがどのようなコードを書けばよいのかわからない。
まずは下記3ファイルを作成しておく。
■View
@ModelType WebApplication1.DefaultModel
@Code
ViewData("Title") = "Index"
End Code
<h2>Index</h2>
@Html.Raw(Model.session.SessionID)
■Model
Public Class DefaultModel
Property session As HttpSessionStateBase
End Class
■Controller
Imports System.Web.Mvc
Namespace Controllers
Public Class DefaultController
Inherits Controller
' GET: Default
Function Index() As ActionResult
Dim model As New DefaultModel
model.session = Session
Return View(model)
End Function
End Class
End Namespace
※面倒であれば、スキャフォールディングアイテムで十分。上記も自動生成したものだ。
方法
1.NugetでMoqのパッケージをインストールする
※必ず、更新プログラムがないか確認すること。もとになっているプロジェクトが最新ではない場合、アセンブリのエラーが発生する。
2.コントトーラーとセッションの関係を理解する
そもそも、Sessionの所有者はだれなのか?セッションはだれが受け渡しを行っているのか。セッションの中身そのものは、Radisのような外部のサーバー、IIS(WorkerProcess)などのWebサーバー、ローカルのCookieなど様々。ただ、それをコントロールしているのは、文字通りController。
Contollerをのぞいてみると、下記のような読み取り専用のプロパティが設定されている。
'
' 概要:
' 現在の HTTP 要求の HttpSessionStateBase オブジェクトを取得します。
'
' 戻り値:
' 現在の HTTP 要求の HTTP セッション状態オブジェクト。
Public ReadOnly Property Session As HttpSessionStateBase
このプロパティをテストコードから何とかして偽装してあげる必要がある。
そこで使用するのが、1でインストールしたMoqというパッケージ。
まずは、コントローラーを偽装する。
Imports Moq
<TestClass()> Public Class UnitTest1
<TestMethod()> Public Sub TestMethod1()
Dim controller As New Mock(Of ControllerContext)
End Sub
End Class
ControllerContextなんて知らないよ、と波線が出た場合はMCVをインストールする。波線にカーソルを当てて、考えられる修正内容候補から選択するなり、テストプロジェクトの参照にMVCを追加するなりすればよい。
※追加するdllのバージョンは注意が必要。テストを行うWebアプリケーションのMVCのバージョンと合わせておくのが良いと思う。
以下が追加されているはずだが、ない場合は自分でimportsする。
Imports System.Web.Mvc
これで、コントローラーのモックが出来上がった。
ただし、中身がないのでこのままでは使用できない。このままだと、ただの箱だからだ。
今作りたいのは、セッションを偽装するためのコントローラー。なので、セッションを先に偽装してやる。
Imports System.Web.Mvc
Imports Moq
<TestClass()> Public Class UnitTest1
<TestMethod()> Public Sub TestMethod1()
Dim session As New Mock(Of HttpSessionStateBase)
Dim controller As New Mock(Of ControllerContext)
End Sub
End Class
HttpSessionStateBaseでまた波線がでるとおもう。これも、System.Web.dllの参照が足りないから。
Mvc同様の手順で追加してやる。
この手の奴は、大体「動かすまで」に苦労する。。
Imports System.Web
Imports System.Web.Mvc
Imports Moq
<TestClass()> Public Class UnitTest1
<TestMethod()> Public Sub TestMethod1()
Dim session As New Mock(Of HttpSessionStateBase)
Dim controller As New Mock(Of ControllerContext)
End Sub
End Class
これでやっと準備が整った。
では早速本題。
1.セッションのプロパティを設定する方法
例として、セッションIDを偽装してみる。
'
' 概要:
' Specifies a setup on the mocked type for a call to a non-void (value-returning)
' method.
'
' パラメーター:
' expression:
' Lambda expression that specifies the method invocation.
'
' 型パラメーター:
' TResult:
' Type of the return value. Typically omitted as it can be inferred from the expression.
'
' 注釈:
' If more than one setup is specified for the same method or property, the latest
' one wins and is the one that will be executed.
Public Function Setup(Of TResult)(expression As Expression(Of Func(Of T, TResult))) As ISetup(Of T, TResult)
Mockのメタデータをのぞいてみると、上記メソッドが使いやすそうなので、こちらを使用する。
※ほかのメソッドを使用したサンプルを紹介しているところもあるが、Stack overflowでの海外のエンジニアの反応を見る限りだと「時代遅れ」のメソッドのようだ。
Dim session As New Mock(Of HttpSessionStateBase)
session.Setup(Function(a) a.SessionID).Returns("MySessionId")
上記で、セッションIDが偽装された。戻り値として「MySessionId]を設定している。
これでセッションの偽装はできたので、次はこのセッションをコントローラーに乗せてやる。
それが下記コード。
Dim controller As New Mock(Of ControllerContext)
controller.Setup(Function(a) a.HttpContext.Session).Returns(session.Object)
コントローラーのコンテキスト.セッションプロパティを偽装して、戻り値として前で作成したセッションを設定した状態。最後にテスト確認を入れると下記となった。
<TestMethod()> Public Sub TestMethod1()
Dim session As New Mock(Of HttpSessionStateBase)
session.Setup(Function(a) a.SessionID).Returns("MySessionId")
Dim controller As New Mock(Of ControllerContext)
controller.Setup(Function(a) a.HttpContext.Session).Returns(session.Object)
Console.WriteLine(session.Object.SessionID)
Assert.AreEqual(session.Object.SessionID, "MySessionId")
End Sub
実行結果
テスト名: TestMethod1
テスト成果: 成功
結果 の標準出力: MySessionId
※Console.WriteLineを入れておくと、テスト時の標準出力が見れる。
2.セッションに新しいプロパティを作成して値を埋め込む方法
上記までの流れである程度想像はつくと思うので、コードのみの紹介とする。
<TestMethod()> Public Sub TestMethod1()
Dim session As New Mock(Of HttpSessionStateBase)
session.Setup(Function(a) a("MyTestJsonSession")).Returns("{a:1,b:1,c:1}")
Dim controller As New Mock(Of ControllerContext)
controller.Setup(Function(a) a.HttpContext.Session).Returns(session.Object)
Console.WriteLine(session.Object("MyTestJsonSession"))
Assert.AreEqual(session.Object("MyTestJsonSession"), "{a:1,b:1,c:1}")
End Sub
実行結果
テスト名: TestMethod1
テスト成果: 成功
結果 の標準出力: {a:1,b:1,c:1}
まとめ
テストの目的は、あくまでも成果物の基本的な正常動作を担保するものだ。なので、セッションを使用できることはあくまでも手段にすぎず、動作を担保するものにはならないので注意してほしい。
コメントを残す