0.前置き

ASP.NET MVCの開発ではコンポーネントテスト(単体テスト、ユニットテスト)が大事です。
# いつでも大事ですけど…。
特にデータアクセスする部分のモックを簡単に作れると、開発の時に幸せになれます。
今回はMoqというモックライブラリーを紹介してみたいと思います。

1.Moqとは

Moqは、Mock-Youと読みます。モッキューですね。なんか若干呼びにくい笑
開発プロジェクトはGoogleCode上にホストされています。こちらです。
Moqは強い型付けでモックを作ることができるので、初心者(?)にも簡単に使い始めることができます。
実際のプロジェクトでも利用していますが、機能的に不足を感じたことはないです。

2.インストール

Moqはnuget経由でインストールできます。
簡単。nuget、素晴らしい。

3.実際に使ってみる

インストールができたので、さっそく使ってみます。

3-1.テスト対象のクラスを作成する

テスト対象のクラスは以下のようです。
ProductLogicが、ProductDaoを経由してDBにアクセスするっていう感じです。

[sourcecode language=”csharp”]
namespace MoqSample
{
/// <summary>
/// テスト対象のクラス
/// </summary>
public class ProductLogic
{
public IProductDao ProductDao { get; set; }

public decimal CalculateTax(int id)
{
var domain = ProductDao.GetItemById(id);
var result = domain.Price * 0.05m;
return result;
}

public void EditProduct(Product item)
{
var id = item.Id;
var domain = ProductDao.GetItemById(id);
domain.Price *= 1.05m;
ProductDao.Insert(domain);
}
}

/// <summary>
/// モックで置き換える予定のDaoのインターフェイス
/// </summary>
public interface IProductDao
{
void Update(Product product);
void Insert(Product product);
Product GetItemById(int id);
List<Product> GetAllItems();
}

public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}
}
[/sourcecode]

つづいて、このProductLogicに対するテストを作成します。

3-2.戻り値があるメソッドのテストをする

最初はテスト対象のメソッドが戻り値を持つ場合です。
この場合はテスト対象の出力値をAssertすればOKです。

[sourcecode language=”csharp”]
[TestMethod]
public void CalculateTaxTest()
{
//Arrange
var dao = new Mock<IProductDao>();
dao.Setup(m => m.GetItemById(1))
.Returns(new Product
{
Id = 1,
Name = "Book",
Price = 300
});
var target = new ProductLogic { ProductDao = dao.Object };

//Act
var result = target.CalculateTax(1);

//Assert
Assert.AreEqual(15, result);
}
[/sourcecode]

3-3.戻り値がvoidのメソッドをテストする

テスト対象が戻り値を持たない場合は、検証をどのように行うか悩んでしまいます。
今回はテスト対象の処理の最後に呼ばれるInsertメソッドに着目し、Insertメソッドに渡された値を検証します。
ここではCallBackを利用し、入ってきた値を検証しています。
[sourcecode language=”csharp”]
[TestMethod]
public void EditProductTest()
{
//Arrange
//DBアクセス部分をモックで作成
var dao = new Mock<IProductDao>();
dao.Setup(m => m.GetItemById(10))
.Returns(new Product
{
Id = 10,
Name = "Book",
Price = 300
});
dao.Setup(m => m.Insert(It.IsAny<Product>()))
.Callback<Product>(p =>
{
//コールバックにてAssertを実行
Assert.AreEqual(10, p.Id);
Assert.AreEqual("Book", p.Name);
Assert.AreEqual(315, p.Price);
});
var target = new ProductLogic { ProductDao = dao.Object };

//Act
target.EditProduct(new Product { Id = 10 });

//Assert(一部)
dao.Verify(m => m.Insert(It.IsAny<Product>()), Times.Once());
dao.Verify(m => m.Update(It.IsAny<Product>()), Times.Never());
}
[/sourcecode]

4.サンプルを書いてみての補足

CallBackで検証コードを入れる場合は、ちゃんとその検証コードが呼ばれたかチェックする必要があります。
もし呼ばれていなかったら、検証コードを作りこんだ意味がないですからね。
そういうわけで、dao.Verifyを使って、モックが何回呼ばれたかをチェックします。

5.サンプルコード

サンプルコードはgithubにホスティングしてあります。
AutoMapperのサンプル
コード本体

6.最後に

最近コンポーネントテストが楽しい。
いまはまだMoqしか試していないので、他のテストフレームワークも触ってみたいですね。
あとJavaScriptのテストフレームワークにも挑戦してみたいです。