MOQ 撰寫 Mock 的測試框架 + 我自己寫的例外測試函式

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 沒什麼關係…

This entry was posted in 團隊開發. Bookmark the permalink.

發表迴響

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / 變更 )

Twitter picture

You are commenting using your Twitter account. Log Out / 變更 )

Facebook照片

You are commenting using your Facebook account. Log Out / 變更 )

連結到 %s