2010-10-11 18 views
6

J'ai ce code dans mon application .NET en utilisant l'application NServiceBus:Test Bus.Send dans une application à l'aide NServiceBus

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

Comment iriez-vous sur l'unité-test d'un tel code?

+0

Demandez-vous de tester 'Bus.Send', ou du code qui envoie des messages? – arootbeer

+0

arootbeer, je pose des questions sur le code de test qui envoie des messages. Le code exact que je veux tester est donné dans la question ci-dessus. – mgamer

+0

J'ai ajouté une modification concernant le test du code de délégué sur place, au cas où vous seriez intéressé. – arootbeer

Répondre

3

Tant que votre variable Bus est que vous pouvez simplement exemple IBus, fournir une implémentation IBus raillé à la classe qui contient cette méthode, et vérifier que Envoyer a appelé (avec le délégué approprié, si vous le désirez) après que la méthode a été appelée pour le test. Au-delà de cela, vous commencez à tester Bus.Send lui-même, ce qui n'est pas quelque chose que vous devriez faire.

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

Il existe deux façons d'aborder tester la logique du délégué: vous pouvez essayer de briser l'arbre d'expression fourni, ou vous pouvez changer à une méthode appelée et le transmettre de cette façon. Je n'ai jamais eu profondément dans les expressions, donc je vais vous donner un exemple de ce dernier:

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

Utilisation:

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

Je ne suis pas au courant d'un récipient qui peut faire l'injection de délégué constructeurs, mais je n'ai pas regardé très dur non plus, ce qui pourrait être une meilleure façon de définir le délégué.

EDIT

Comme je l'ai récemment rencontré ce problème dans mes propres tests, et je ne aime pas vraiment avoir à tirer la méthode sur dans son propre constructeur. Je me suis donc mis à découvrir si je pouvais tester le délégué "en place". Il se avère que vous pouvez:

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

Il y a des compromis ici - tirer le constructeur de message dans une interface est certainement plus conforme à la SRP que de le laisser en tant que délégué en ligne (comme le test démontre clairement: vous devez Agir deux fois pour tester tout le code). Cependant, il présente des avantages à la fois en termes de taille de code et de lisibilité.

En outre, ce code est spécifique au Moq; Je ne sais pas s'il est possible de récupérer le délégué d'un simulacre de RhinoMocks, ou quelle serait la syntaxe pour cela.

+1

Si le code en cours de test est un gestionnaire de messages, vous pouvez utiliser la bibliothèque NServiceBus.Testing qui peut se moquer du bus pour vous, et si vous utilisez l'injection Setter pour la propriété IBus, elle l'injectera également dans votre gestionnaire. –

+0

arootbeer, la partie la plus difficile pour moi est de savoir comment vérifier que la méthode Send a été appelée avec le délégué approprié? Rhino Mocks propose une méthode AssertWasCalled qui accepte les contraintes sur les arguments, mais y at-il une contrainte vous permettant de vérifier si la méthode a été appelée avec un délégué approprié? – mgamer

+0

Vous pouvez essayer d'utiliser GetArgumentsForCallsMadeOn pour retirer le délégué et inspecter ses propriétés: http://kashfarooq.wordpress.com/2009/01/10/rhino-mocks-and-getargumentsforcallsmadeon/ – PatrickSteele

2

Vérifiez le cadre de test fourni avec NSB, il utilise Rhino sous: http://nservicebus.com/UnitTesting.aspx. Il y a plusieurs exemples dans le téléchargement qui l'utilisent avec NUnit. Vous pouvez aussi l'utiliser sous MSTest.