2012-02-22 72 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

相关问题