模拟验证交互

时间:2011-06-30 09:09:11

标签: c# .net unit-testing mocking rhino-mocks

通常当我需要模拟一个类进行测试时,我会使用像Rhino Mocks这样的库。在这里,我有一个名为MyService的课程,需要IEmailSender

public class MyService
{
    private readonly IEmailSender sender;

    public MyService(IEmailSender sender)
    {
        this.sender = sender;
    }

    public void Start()
    {
        this.sender.SendEmail();
    }
}

如果我需要测试这两个对象之间的交互,我的测试看起来像这样:

[TestMethod]
public void Start_Test_Using_Rhino_Mocks()
{
    IEmailSender emailSender = MockRepository.GenerateMock<IEmailSender>();

    MyService service = new MyService(emailSender);
    service.Start();

    emailSender.AssertWasCalled
        (
            x => x.SendEmail(),
            c => c.Repeat.Once()
        );
}

在上面的测试中,我使用Rhino Mocks生成模拟并断言SendEmail()方法被调用一次。

但是,如果我不能使用Rhino Mocks并且必须创建手动模拟怎么办?

public class MockEmailSender : IEmailSender
{
    public void SendEmail()
    {
    }
}

[TestMethod]
public void Start_Test_Using_Manual_Mocks()
{
    MockEmailSender emailSender = new MockEmailSender();

    MyService service = new MyService(emailSender);
    service.Start();

    // How do I test the interaction?
}

使用我手动创建的模拟,如何验证是否调用了SendEmail()方法?我可以将我的断言放在模拟的SendEmail()方法中,但是这会使测试很难理解,因为当我看到测试时我没有立即看到发生了什么。

3 个答案:

答案 0 :(得分:4)

一个非常简单的解决方案是让您的手动模拟只是一个州的持有者,并带有用于调用每个方法的计数器。但它很脆弱......

public class MockEmailSender : IEmailSender
{
    public int SendCount = 0;

    public void SendMail(...)
    {
        SendCount++;
    }

    // ... other IEmailSender methods ...
}

然后在调用方法后查询SendCount,并确保它是== 1.

请记住,Rhino Mocks正在为您动态创建 - 如果您手动执行此操作,则必须手动在编译时对接口更改做出反应。

答案 1 :(得分:1)

我认为除了在“SendEmail()”中设置标志之外别无其他选择,并且从测试中检查该标志是否为MockEmailSender的新方法,如“sendMailWasInvoked()”或其他东西像这样(实际上是一种“验证”)。 您可以对此进行扩展以计算调用次数,参数...

答案 2 :(得分:0)

我建议不要创建任何手动模拟(因为如果你向界面添加新方法,它会被破坏)。

如果你真的必须这样做,当你在MockEmailSender中暴露一些counter / bool时你可以稍后断言。

Assert.IsTrue(emailSender.IsCalled)