MOQ The simplest mocking library for .NET 3.5 and Silverlight with deep C# 3.0 integration 看起來還蠻有趣的,想寫 Mock 的人可以參考一下。
我自己先前也有為了測 Exception 而使用了 lambda expression,覺得 lambda expression 是個全新的寫程式方法,一開始看起來會比較難理解,但有時可以減少很多程式碼…減少程式碼又會讓程式碼較易讀…
標準的測試寫法,在待測函式會丟出預期中的例外時,要使用 [ExpetationException],但只能一個 TestMethod 測一個例外。我寫的這個功能,是為了在同一個 TestMethod 中可以測試多個會丟出例外的目標函式。
程式碼很短,貼出來供參考:
正常的連續測試例外的寫法會是這樣:
[TestMethod]
public void TestThrowExceptions()
{
Foo foo = new Foo();
try
{
foo.ThrowArgumentException();
Assert.Fail();
}
catch (Exception ex)
{
if (ex.GetType() != typeof(ArgumentException))
Assert.Fail();
}
try
{
foo.ThrowArgumentNullException();
Assert.Fail();
}
catch (ArgumentException ex)
{
if (ex.GetType() != typeof(ArgumentNullException))
Assert.Fail();
}
}
非常地長…而且重覆的地方很多…但 try/catch 樣式又沒辦法包在共用的子函式中重覆呼叫,因為裡面有 foo.xxx() 會每次都不一樣…這就令我想到: 匿名函式可以上場了…
以下是可以重覆使用的測試函式:
public static void Throws(Type exceptionType, Action action)
{
if (exceptionType == null)
{
throw new ArgumentNullException("exceptionType");
}
if (exceptionType != typeof(Exception) && !exceptionType.IsSubclassOf(typeof(Exception)))
{
throw new ArgumentException("exceptionType 參數必須為一例外型別。", "exceptionType");
}
try
{
action();
}
catch (Exception actualException)
{
if (actualException.GetType() != exceptionType)
{
Assert.Fail("預期要丟出例外 {0},但卻丟出例外 {1}。此例外之內容為: {2}", exceptionType.ToString(), actualException.GetType(), actualException.ToString());
}
return;
}
Assert.Fail("預期要丟出例外 {0},但卻完成執行,沒有例外。", exceptionType.ToString());
}
測試的寫法為:AssertExtension.Throws(typeof(ArgumentNullException),() => foo.Bar());
後來看到這個 MOQ 後,發現使用 Generic Method 更簡單,於是改寫如下:
public static void Throws<T>(Action action) where T : Exception
{
try
{
action();
}
catch (Exception actualException)
{
if (actualException.GetType() != typeof(T))
{
Assert.Fail("預期要丟出例外 {0},但卻丟出例外 {1}。此例外之內容為: {2}", typeof(T).ToString(), actualException.GetType(), actualException.ToString());
}
return;
}
Assert.Fail("預期要丟出例外 {0},但卻成功完成執行,沒有例外。", typeof(T).ToString());
}
測試的寫法為:AssertExtension.Throws<ArgumentNullException>(() => foo.Bar());
因為在原來的寫法,丟入 exceptionType 有可能不是 Exception 類別,因此要在執行時期做檢查。新的寫法,若是不正確的類別,會無法通過編譯…因此就不必做執行期檢查。而且測試的寫法也更精簡一點。
所以連續測試丟出例外的 TestMethod 可能是這樣:[TestMethod]
public void TestFooToInt()
{
Foo foo = new Foo();
AssertExtension.Throws<ArgumentNullException>(() => foo.ToInt(null));
AssertExtension.Throws<ArgumentOutOfRangeException>(() => foo.ToInt("a"));
Assert.AreEqual(1, foo.ToInt("1"));
Assert.AreEqual(-1, foo.ToInt("-1"));
}
這樣就可以在同一個 TestMethod 用很簡單的寫法測會丟出例外的函式了。
一開始寫 MOQ,到後面全部是寫我自己的東西,跟 MOQ 沒什麼關係…