使用NServiceBus测试Bus.Send应用程序

时间:2010-10-11 18:49:17

标签: c# unit-testing mocking rhino-mocks nservicebus

我在使用NServiceBus的应用程序.NET应用程序中使用此代码:

 Bus.Send<IServiceStarted>(e =>
                             {
                                  e.ServiceInfo = ReadServiceInfo();
                                  e.EventTime = DateProvider.Now;
                             });

你会如何对这样的代码进行单元测试?

2 个答案:

答案 0 :(得分:3)

只要您的Bus变量是IBus实例,您就可以向包含此方法的类提供模拟的IBus实现,并验证是否在其上调用了Send (在适当的委托下,如果你愿意的话)在调用方法进行测试之后。除此之外,您正在测试Bus.Send本身,这不是您应该做的事情。

public class ClassThatSendsMessages
{
    private readonly IBus _bus;
    public ClassThatSendsMessages(IBus bus /*, ... */)
    {
        _bus = bus;
        /* ... */
    }

    public void CodeThatSendsMessage()
    {
        /* ... */
        _bus.Send<IMyMessage>(mm => mm.Property1 = "123");
        /* ... */
    }
}

public class ClassThatTestsClassThatSendsMessages
{
    public void CallCodeThatSendsMessage()
    {
        //Mock IBus object here
        var objectToTest = new ClassThatSendsMessages(mockedIBus /*, ... */)

        objectToTest.CodeThatSendsMessage();

        //Verify that IBus mock's Send() method was called
    }
}

有两种方法可以测试委托的逻辑:您可以尝试分解提供的表达式树,或者可以将其更改为命名方法并以此方式传递。我从来没有深入到表达式中,所以我将提供后者的一个例子:

public static class MyMessageBuilder
{
    public static void Build(IMyMessage message) { /* ... */ }
}

public class ClassThatTestsMyMessageBuilder
{
    public void CallCodeThatBuildsMessage()
    {
        var message = Test.CreateInstance<IMyMessage>(MyMessageBuilder.Build);

        //Verify message contents
    }
}

用法:

public class ClassThatSendsMessages
{
    private readonly IBus _bus;
    public static Action<IMyMessage> BuildMessage { private get; set; }
    public ClassThatSendsMessages(IBus bus /*, ... */)
    {
        _bus = bus;
        /* ... */
    }

    public void CodeThatSendsMessage()
    {
        /* ... */
        _bus.Send<IMyMessage>(mm => BuildMessage (mm));
        /* ... */
    }
}

我不知道一个可以向构造函数委托注入的容器,但是我也没有看起来很难,所以这可能是设置委托的更好方法。

修改

由于我最近在自己的测试中遇到过这个问题,我真的不想将方法拉到自己的构建器中。所以我开始发现是否可以“就地”测试代表。事实证明你可以:

public class ClassThatTestsClassThatSendsMessages
{
    public void CallCodeThatSendsMessage()
    {
    Action<IMyMessage> messageAction = null;

    //Mock IBus object here
    mockedIBus.Setup(b => b.Send(Args.IsAny<Action<IMyMessage>>()))
        .Callback((Action<IMyMessage> a) => messageAction = a);
    var myMessage = Test.CreateInstance<IMyMessage>();

    var objectToTest = new ClassThatSendsMessages(mockedIBus /*, ... */)

    //Run the code that sends the message
    objectToTest.CodeThatSendsMessage();
    //Run the code that populates the message
    messageAction(myMessage);

    //Verify expectations on Setups
    //Verify the message contents;
    }
}

这里存在权衡 - 将消息构建器拉出到接口当然更符合SRP而不是将其作为内联委托(因为测试清楚地证明:为了测试所有代码,您必须执行两次)。但是,它在代码大小和可读性方面都有好处。

此外,代码是特定于Moq的;我不知道是否可以从RhinoMocks模拟器中获取代理,或者语法是什么。

答案 1 :(得分:2)

查看NSB附带的测试框架,它使用Rhino下面的http://nservicebus.com/UnitTesting.aspx。下载中有几个与NUnit一起使用的示例。你也可以在MSTest下工作。