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.
Stai chiedendo di testare 'Bus.Send', o codice che invia messaggi? – arootbeer
arootbeer, sto chiedendo di testare il codice che invia messaggi. Il codice esatto che voglio testare è indicato nella domanda sopra. – mgamer
Ho aggiunto una modifica per testare il codice delegato sul posto, nel caso siate interessati. – arootbeer