2012-02-22 25 views
1

我的測試類在其構造函數中使用了2個對象,一個數據加載器和一個使用數據加載器返回的數據的類。驗證在函數調用中傳遞的方法是否正確

數據加載器接口有2個函數,LoadCompanies()和LoadEmployees(),它們都接受一個int參數並返回一個IEnumerable。

如何驗證被測方法是否將LoadCompanies()和非LoadEmployees()傳入數據使用者類?

這裏是我的代碼:

[TestFixture] 
public class TestingFunctionalParameters_UT 
{ 
    [Test] 
    public void Correct_Loader_Method_is_Used() 
    { 
     const int userId = 1; 
     var companies = new[] { "c1", "c2" }; 
     var dataLoader = MockRepository.GenerateMock<ITestDataLoader>(); 
     var dataConsumer = MockRepository.GenerateMock<IDataConsumerClass>(); 

     var testObject = new TestClass(dataLoader, dataConsumer); 

     dataConsumer.Expect(fc => fc.LoadIt(Arg<Func<IEnumerable<string>>>.Is.TypeOf)).Return(true); 

     //TODO: validate that correct dataloader function was called... 
     //dataLoader.Expect(dl => dl.LoadCompanies(userId)).Return(companies); 

     var result = testObject.Run(userId); 

     Assert.That(result, Is.True); 
     dataLoader.VerifyAllExpectations(); 
     dataConsumer.VerifyAllExpectations(); 
    } 
} 

public class TestClass 
{ 
    private readonly ITestDataLoader dataLoader; 
    private readonly IDataConsumerClass funcClass; 

    public TestClass(ITestDataLoader dataLoader, IDataConsumerClass funcClass) 
    { 
     this.dataLoader = dataLoader; 
     this.funcClass = funcClass; 
    } 

    public bool Run(int userId) 
    { 
     Func<IEnumerable<string>> loadFn =() => dataLoader.LoadCompanies(userId); 
     return funcClass.LoadIt(loadFn); 
    } 
} 

public interface ITestDataLoader 
{ 
    IEnumerable<string> LoadCompanies(int userId); 
    IEnumerable<string> LoadEmployees(int userId); 
} 

public interface IDataConsumerClass 
{ 
    bool LoadIt(Func<IEnumerable<string>> load); 
} 
+0

爲什麼你要這麼做?我寧願製作參數的類型ITestDataLoader並調用所需的方法。 – haiyyu 2012-02-22 16:27:54

+0

我假設這是一個更復雜的委託注入模式的簡化例子。 – 2012-02-22 16:49:53

+0

你是對的Steve。我發現我一遍又一遍地做同樣的事情來創建一個報告:加載數據(每次不同的方法),使用另一個對象返回一些項目,然後使用第三個對象來生成一個報告(每次不同) 。所以我重構了:將共同的邏輯拉成一個私人方法,以函數或動作作爲參數。然後我用自己的測試把這個私有方法變成另一個類。它真的簡化了測試的負擔。 – 2012-02-23 14:35:36

回答

0

編輯:我假設你的例子是簡化的一個,你的實際執行是試圖測試代理注入模式)

也許你可以寫你的測試樣本這是嗎? (編輯以實際編譯

[Test] 
public void Correct_Loader_Method_is_Used() 
{ 
    const int userId = 1; 
    var companies = new[] { "c1", "c2" }; 
    var dataLoader = MockRepository.GenerateMock<ITestDataLoader>(); 
    var dataConsumer = MockRepository.GenerateMock<IDataConsumerClass>(); 

    var testObject = new TestClass(dataLoader, dataConsumer); 

    dataConsumer.Expect(fc => fc.LoadIt(Arg<Func<IEnumerable<string>>>.Matches(x => x().Any()))).Return(true); 

    //validate that correct dataloader function was called... 
    dataLoader.Expect(dl => dl.LoadCompanies(userId)).Return(companies); 
    // Fails if you uncomment this line 
    //dataLoader.Expect(dl => dl.LoadEmployees(userId)).Return(companies); 

    var result = testObject.Run(userId); 

    Assert.That(result, Is.True); 
    dataLoader.VerifyAllExpectations(); 
    dataConsumer.VerifyAllExpectations(); 
} 

基本上匹配()約束將嘗試執行的方法,如果它試圖調用LoadEmployees(),RhinoMocks會抱怨,因爲它並沒有定義的模擬。

更新:處理Action<T>代表

這可能是有點不太強勁,但對於Action<T> S:

public interface IDataConsumerClass 
{ 
    bool LoadIt(Func<IEnumerable<string>> load); 
    bool ExecuteIt<T>(Action<T> execute); 
} 

//... 

dataConsumer.Expect(fc => fc.ExecuteIt(Arg<Action<int>>.Matches(x => ActionWrapper(x, userId)))).Return(true); 

//... 

private bool ActionWrapper<T>(Action<T> action, T arg) 
{ 
    action(arg); 
    return true; 
} 
+0

Steve,這就是我正在尋找的......想出一個方法來讓測試進行dataLoader調用。我試圖用你的建議進行測試,但代碼沒有建立。我得到錯誤「帶聲明主體的lambda方法不能轉換爲表達式樹」。我不明白這意味着什麼。 – 2012-02-23 14:43:50

+0

就像我說過的,我沒有嘗試過,但現在我會。支持... – 2012-02-23 15:02:14

+0

修正了它。訣竅是強制你正在測試的方法返回值爲一個布爾值。我用IEnumerable 。任何()來做到這一點。你只需要確保你的模擬返回值至少返回一個值。 – 2012-02-23 15:11:58

1

您可以創建企業和員工類

class Company 
{ 
    public Company(string name) 
    { 
     Name = name; 
    } 

    public string Name { get; private set; } 

    public override string ToString() 
    { 
     return Name; 
    } 
} 

執行相同的員工,然後像這樣定義

public interface ITestDataLoader 
{ 
    IEnumerable<Company> LoadCompanies(int userId); 
    IEnumerable<Employee> LoadEmployees(int userId); 
} 

你的界面現在公司和員工不能再困惑了。


編輯:

如果你有很多這樣的情況下,你可以創建一個公共基類

​​
+0

奧利弗,製作單獨的課程比這個問題需要更多的工作。我剛剛使用IEnumerable 作爲例子 - 它可能是任何具有相同簽名的方法。我只想要一種方法來驗證是否有正確的呼叫。不過謝謝。 – 2012-02-23 14:48:24

+0

我的解決方案試圖從一開始就避免可能的錯誤,而不是通過測試來保證正確的行爲。但我同意,它更復雜。 – 2012-02-23 19:27:57

相關問題