【问题标题】:Testing Bus.Send in an application using NServiceBus使用 NServiceBus 在应用程序中测试 Bus.Send
【发布时间】:2011-04-23 23:16:28
【问题描述】:

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

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

您将如何对这样的代码进行单元测试?

【问题讨论】:

  • 你问的是测试Bus.Send,还是发送消息的代码?
  • arootbeer,我在询问发送消息的测试代码。我想测试的确切代码在上面的问题中给出。
  • 我添加了一个关于就地测试委托代码的编辑,以防您感兴趣。

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


【解决方案1】:

只要您的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(正如测试清楚地表明:您必须执行两次才能测试所有代码)。但是,它在代码大小和可读性方面都有好处。

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

【讨论】:

  • 如果正在测试的代码是一个消息处理程序,那么您可以使用可以为您模拟总线的 NServiceBus.Testing 库,如果您对 IBus 属性使用 setter 注入,它将注入它也进入你的处理程序。
  • arootbeer,对我来说困难的部分是如何验证 Send 方法是否被适当的委托调用? Rhino Mocks 提供了一个 AssertWasCalled 方法,它接受对参数的约束,但它有一个约束允许您验证是否使用适当的委托调用方法?
  • 您可以尝试使用 GetArgumentsForCallsMadeOn 拉出委托并检查其属性:kashfarooq.wordpress.com/2009/01/10/…
【解决方案2】:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-19
    • 2011-11-16
    • 1970-01-01
    • 1970-01-01
    • 2018-07-07
    • 1970-01-01
    相关资源
    最近更新 更多