2010-10-11 7 views
6

ho questo codice nel mio app applicazione .NET utilizzando NServiceBus:test Bus.Send in un'applicazione utilizzando NServiceBus

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

Come si va sulla unit-test di un tale codice?

+0

Stai chiedendo di testare 'Bus.Send', o codice che invia messaggi? – arootbeer

+0

arootbeer, sto chiedendo di testare il codice che invia messaggi. Il codice esatto che voglio testare è indicato nella domanda sopra. – mgamer

+0

Ho aggiunto una modifica per testare il codice delegato sul posto, nel caso siate interessati. – arootbeer

risposta

3

Finché la variabile Bus è un'istanza IBus, si può semplicemente fornire un deriso IBus implementazione alla classe che contiene questo metodo, e verificare che Invia è stato chiamato su di esso (con il delegato del caso, se lo desiderano) dopo che il metodo è stato invocato per il test. Oltre a ciò, stai entrando in testing con lo stesso Bus.Send, che non è qualcosa che dovresti fare.

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 
    } 
} 

Ci sono due modi di affrontare il test la logica del delegato: si può provare ad abbattere l'albero espressione fornito, o si può cambiare ad un metodo chiamato e passare in questo modo. Non ho mai ottenuto in profondità in espressioni, quindi mi forniscono un esempio di questi ultimi:

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 
    } 
} 

Usage:

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)); 
     /* ... */ 
    } 
} 

Io non sono a conoscenza di un contenitore che può fare l'iniezione delegato costruttori, ma poi non sono nemmeno stato molto difficile, quindi questo potrebbe essere un modo ancora migliore per impostare il delegato.

EDIT

Come ho recentemente eseguito in questo problema nelle mie prove, e non mi piace dover tirare fuori il metodo nella propria costruttore. Così ho deciso di scoprire se potevo testare il delegato "sul posto". Si scopre che è possibile:

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; 
    } 
} 

Non ci sono compromessi qui - tirando il costruttore messaggio fuori in un'interfaccia è sicuramente più compatibile con SRP di lasciarlo come un delegato in linea (come il test dimostra chiaramente: bisogna Agisci due volte per testare tutto il codice). Tuttavia, presenta vantaggi sia in termini di dimensioni del codice che di leggibilità.

Inoltre, questo codice è specifico per Moq; Non so se sia possibile recuperare il delegato da una simulazione di RhinoMocks o quale sarebbe la sintassi per quello.

+1

Se il codice in fase di test è un gestore di messaggi, è possibile utilizzare la libreria NServiceBus.Testing che può prendere in giro il bus e, se si utilizza l'iniezione setter per la proprietà IBus, verrà iniettata anche nel gestore. –

+0

arootbeer, la parte più difficile per me è come verificare che il metodo di invio sia stato chiamato con il delegato appropriato? Rhino Mocks offre un metodo AssertWasCalled che accetta i vincoli sugli argomenti, ma esiste un vincolo che consente di verificare se il metodo è stato chiamato con un delegato appropriato? – mgamer

+0

Si potrebbe provare a utilizzare GetArgumentsForCallsMadeOn per estrarre il delegato e ispezionarne le proprietà: http://kashfarooq.wordpress.com/2009/01/10/rhino-mocks-and-getargumentsforcallsmadeon/ – PatrickSteele

2

Controlla il framework di test fornito con NSB, utilizza Rhino al di sotto: http://nservicebus.com/UnitTesting.aspx. ci sono diversi esempi nel download che lo usano con NUnit. Puoi farlo funzionare anche con MSTest.

Problemi correlati