2010-09-20 72 views
4

有一個功能:如何用Moq測試lambda函數?

public class MyCacheClass : ICache 
{ 
    public void T GetObject<T>(Func<T> func) 
    { 
     T t; 
     ... 
     t = func(); 
     ... 
     return t; 
    } 
} 

public class MyWorkClass : IWork 
{ 
    public Object MyWorkMethod(string value) 
    { 
     return new object(); 
    } 
} 

這些功能被稱爲以下列方式:

public class MyTestableClass 
{ 
    public void MyTestableFunc(ICache cache, IWorkClass work) 
    { 
     string strVal="..."; 
     ... 
     Object obj = cache(()=>work.MyWorkMethod(strVal)); 
     ... 
    } 
} 

有必要寫單元測試(有MOQ)爲並檢查是否適當的參數作爲傳入'MaCacheClass.GetObject'。

應該是這樣的:

[TestMethod] 
public void MyTest() 
{ 
    Mock<ICache> mockCache = new Mock<ICache>(); 
    Mock<IWorkClass> mockWorkClass = new Mock<IWorkClass>(); 

    MyTestableClass testable = new MyTestableClass(); 
    testable.MyTestableFunc(mockCache.Object, mockWorkClass.Object); 

    // should I check if 'MyCacheClass' was called with proper parameter? 
    mockCache.Verify(mock=>mock.GetObject(...)).Times.Once()); 
} 

我怎麼可能提供的參數,將適合作爲「拉姆達功能」? 還有其他的選擇嗎?

歡迎任何想法。

回答

1

其中一個選項可能是:而不是在功能該參數傳遞一些對象,實施以「GetObject的」功能接口,例如:

public interface IGetObject 
{ 
    T GetObject<T>(); 
} 

public class MyGetObject : IGetObject 
{ 
    public MyGetObject() 
    { 
    } 

    public void Init(string strVal, Func<string, T> func) 
    { 
     _strVal = strVal; 
     _func = func; 
    } 

    public T GetObject<T>() 
    { 
     return _func(_strVal); 
    } 
} 

public class MyCacheClass : ICache 
{ 
    public void T GetObject<T>(IGetObject getter) 
    { 
     T t; 
     ... 
     t = getter.GetObject<T>(); 
     ... 
     return t; 
    } 
} 

public class MyTestableClass 
{ 
    public void MyTestableFunc(ICache cache, IWorkClass work) 
    { 
     string strVal="..."; 
     IGetObject getter = UnityContainer.Resolve<IGetObject>(); 
     getter.Init(strVal, str=>work.MyWorkMethod(str)); 
     ... 
     Object obj = cache.GetObject(getter); 
     ... 
    } 
} 

在這種情況下,我想,我們也需要注入UnityContainer爲測試對象,因此這將是類似的東西:

[TestMethod] 
public void MyTest() 
{ 
    Mock<ICache> mockCache = new Mock<ICache>(); 
    Mock<IWorkClass> mockWorkClass = new Mock<IWorkClass>(); 
    Mock<IGetObject> mockGetter = new Mock<IGetObject>(); 

    mockGetter.Setup(mock=>mock.GetObject()).Return(new Object()); 

    MyTestableClass testable = new MyTestableClass(); 
    testable.MyTestableFunc(mockCache.Object, mockWorkClass.Object); 

    // should I check if 'MyCacheClass' was called with proper parameter? 
    mockCache.Verify(mock=>mock.GetObject(mockGetter.Object)).Times.Once()); 
} 

,併爲「MyCacheClass」的「GetObject的」方法中的額外的測試,如果它稱之爲「GetObject的」的IGetObject參數的方法,將檢查...

P.S.有點複雜的解決方案...所以,如果有人看到更好的解決方案,請告知!

1

您可以更改類,以便將lambda傳遞到類中,而不是整個IWorkClass。然後你就可以存儲和驗證實際的lambda。

或者,您可以使用回調機制來實際調用傳入緩存的lambda,然後驗證IWorkClass.MyWorkMethod是否被該模擬調用。這與緩存和工作類如何最終得到使用相匹配,何時緩存是有價值的,因此它可以提供比單獨測試方法更有價值的行爲的更好例子。第三,你可以只使用It.IsAny<Func<string, void>>(),並接受你必須通過檢查才能得到這一點(只要它更容易得到它,而不是錯誤,這通常是好的)。我通常命名我的委託或lambda簽名,所以我可能有這個Func<string, void>事情是錯誤的。它會解決。

作爲第四種選擇,推出一個小存根緩存而不是使用模擬框架,然後從中獲取並調用lambda。儘管我喜歡Moq及其Java對應文件Mockito,但有時我發現單元測試更容易閱讀和維護,因爲我們只是接受這些工具不支持我們正在嘗試使用的工具。這只是幾行代碼,而且有可能這個存根緩存對其他單元測試也是有用的。