Moq探訪~その1~:Moqとは何か?Mockとは何か?そしてMockの生成 #adcjcsに続く第2回目。
前回はMockとは何か?Moqとは何か?Moqを使ったMockオブジェクトの作成方法について紹介しました。
今回は、先に進む前になぜMockライブラリを使うのか、という話をしようと思います。
従来のMockオブジェクト作成方法
今のように便利なMockライブラリができる前も、「Mock」という考えはありました。その実現方法は、
サブクラス化して、それぞれのメソッドをオーバーライドする
または
interfaceを実装し、それぞれのメソッドを記述する
というものでした。
例を次に示します。
・Mock対象interface
public interface IMyInterface
{
void doSomething();
}
・Mockクラス
public class MyMock : IMyInterface
{
public void doSomething()
{
// 偽装した処理
}
}
・Mockの使用方法
class Program
{
static void Main(string[] args)
{
// Mockオブジェクトの作成
var mock = new MyMock();
// Mockの使用
mock.doSomething();
}
}
従来の方法の問題
サブクラスまたは実装クラスを使ってMockを作成する方法には、大きく2つの問題があります。
- Mock対象のメソッドが多い場合、Mockクラスのコードが膨大になる。
- 偽装したい処理ごとにいくつもMockクラスを作る必要がある。
1.巨大なMockコード
上で説明したような、メソッドが1つだけのようなinterfaceなら良いのですが、多くのメソッドを持つinterfaceを偽装する場合、そのすべてのメソッドを実装しなければなりません。
したがって、偽装したいメソッド以外にも、余計なコードが必要です。
以下に、IListインターフェースの例を示します。
public class MyList : IList
{
public int Add(object value)
{
return 0;
}
public void Clear()
{
}
public bool Contains(object value)
{
return false;
}
public int IndexOf(object value)
{
return 0;
}
public void Insert(int index, object value)
{
}
public bool IsFixedSize
{
get { return true; }
}
public bool IsReadOnly
{
get { return true; }
}
public void Remove(object value)
{
}
public void RemoveAt(int index)
{
}
private object[] objects;
public object this[int index]
{
get
{
return objects[index];
}
set
{
objects[index] = value;
}
}
public void CopyTo(Array array, int index)
{
}
// このプロパティだけ偽装したい
public int Count
{
get { return 0; }
}
public bool IsSynchronized
{
get { return false; }
}
public object SyncRoot
{
get { return null; }
}
public IEnumerator GetEnumerator()
{
return objects.GetEnumerator();
}
}
2.複数のMockクラスが必要
偽装したい処理が少なければまだよいのですが、0を返す場合、1を返す場合、例外を発生させる場合、・・・と様々な処理を偽装したい場合、その処理ごとにMockクラスを作成する必要があります。
以下に例を示します。
・Mock対象inteface
public interface IMyInterface
{
int GetCount();
}
・Mockクラス
// 0を返すMock
public class MyMockReturnZero : IMyInterface
{
public int GetCount()
{
return 0;
}
}
// 1を返すMock
public class MyMockReturnOne : IMyInterface
{
public int GetCount()
{
return 1;
}
}
// 例外をthrowするMock
public class MyMockThrowException : IMyInterface
{
public int GetCount()
{
throw new Exception("例外処理の偽装");
}
}
Mockライブラリを使った解決
Mockライブラリを使うと、上記の問題を解決し、コード量を大幅に削減することができます。
1.Mockオブジェクト専用の実装クラスが不要
前回紹介したように、Moqを使ってMockオブジェクトを作成するには、これだけです。
// Mockの作成
var mock = new Mock<IMyInterface>();
// Mockオブジェクトの取得
var obj = mock.Object;
2.複数の処理を偽装したMockの作成が容易
Moqを使って複数の処理を偽装するには、次のように書きます。
・Mock作成コード
public static void Main(string[] args)
{
// Mockの作成
var mock = new Mock<IMyInterface>();
// 0を返す
mock.Setup(m => m.GetCount()).Returns(0);
Console.WriteLine(mock.Object.GetCount());
// 1を返す
mock.Setup(m => m.GetCount()).Returns(1);
Console.WriteLine(mock.Object.GetCount());
// 例外を発生させる
mock.Setup(m => m.GetCount()).Throws(new Exception("例外を発生させる"));
try
{
mock.Object.GetCount();
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
・実行結果
まとめ
- MockライブラリがなくてもMockを作成することは可能。
- Mockライブラリを使うことで、シンプルにMockを記述できるようになる。
次回以降の予定
Mockライブラリの利点を紹介したところで、次からは実際にいろいろなMoqの機能を紹介していきます。