2010-10-11 33 views
6

應用程序使用我有NServiceBus在我的應用程序.NET應用程序的代碼:測試Bus.Send使用NServiceBus

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

你會如何單元測試這樣的代碼?

+0

你問的是測試'Bus.Send',還是發送消息的代碼? – arootbeer 2010-10-11 19:01:50

+0

arootbeer,我正在詢問測試發送消息的代碼。我想要測試的確切代碼在上面的問題中給出。 – mgamer 2010-10-11 20:35:53

+0

我添加了一個關於就地測試委託代碼的編輯,以防您感興趣。 – arootbeer 2010-10-18 15:14:06

回答

3

只要你Bus變量是IBus例如,你可以簡單地提供一個嘲笑IBus實現包含此方法的類,並驗證發送被稱爲它(與適當的代表,如果你願意的話)該方法被調用後進行測試。除此之外,你正在測試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

如果正在測試的代碼是消息處理程序,那麼您可以使用NServiceBus.Testing庫來爲您模擬總線,如果您使用setter注入了IBus屬性,它也會將它注入到您的處理程序中。 – 2010-10-12 05:16:23

+0

arootbeer,對我來說難的部分是如何驗證Send方法是否與相應的委託調用? Rhino Mocks提供了一個方法AssertWasCalled,它接受參數的約束,但是它有一個約束,允許您驗證方法是否使用適當的委託來調用? – mgamer 2010-10-12 06:34:55

+0

您可以嘗試使用GetArgumentsForCallsMadeOn來提取委託並檢查其屬性:http://kashfarooq.wordpress.com/2009/01/10/rhino-mocks-and-getargumentsforcallsmadeon/ – PatrickSteele 2010-10-12 12:01:55