模拟以验证交互

本文关键字:交互 验证 模拟 | 更新日期: 2023-09-27 18:00:50

通常,当我需要模拟一个类进行测试时,我会使用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生成mock,并断言SendEmail()方法被调用过一次。

但是,如果我不能使用Rhino Mocks,而不得不创建手动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?
}

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

模拟以验证交互

一个非常简单的解决方案是让您的手动mock只是一个状态持有者,并为每个方法的调用提供计数器。但它很脆弱。。。

public class MockEmailSender : IEmailSender
{
    public int SendCount = 0;
    public void SendMail(...)
    {
        SendCount++;
    }
    // ... other IEmailSender methods ...
}

然后,只需在进行方法调用后查询SendCount,并确保其为==1。

记住,Rhino Mocks是为您动态创建的——如果您手动创建,则必须在编译前手动对接口更改做出反应。

我认为除了在"SendEmail(("中设置标志,并通过MockEmailSender的新方法(如"sendMailWasInvoked(("或类似的方法(从测试中检查该标志(实际上是一种"验证"(,您别无选择。您可以将其扩展为计算调用次数、参数。。。

我建议不要创建任何手动Mock(因为如果向接口添加新方法,它就会被破坏(。

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

Assert.IsTrue(emailSender.IsCalled)