Тестирование Bus.Send в приложении с помощью NServiceBus - PullRequest
6 голосов
/ 11 октября 2010

У меня есть этот код в моем приложении .NET-приложении, использующем NServiceBus:

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

Как бы вы провели модульное тестирование такого кода?

Ответы [ 2 ]

3 голосов
/ 12 октября 2010

Пока ваша переменная 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, или какой будет синтаксис для этого.

2 голосов
/ 12 октября 2010

Проверьте тестовый фреймворк, который поставляется с NSB, он использует Rhino: http://nservicebus.com/UnitTesting.aspx. есть несколько примеров в загрузке, которые используют его с NUnit. Вы также можете заставить его работать под MSTest.

...