2010-01-14 3 views
5

Ich habe gerade begonnen, Komponententests (unter Verwendung von xUnit und Moq) auf einem bereits bestehenden Projekt von mir zu implementieren. Das Projekt verwendet weitgehend die Abhängigkeitsinjektion über den Einheitscontainer.Moq und Zugriff auf die aufgerufenen Parameter

Ich habe zwei Dienste A und B. Service A wird in diesem Fall getestet. Dienst A ruft B auf und gibt ihm einen Delegaten für eine interne Funktion. Dieser "Rückruf" wird verwendet, um A zu benachrichtigen, wenn eine Nachricht empfangen wurde, die verarbeitet werden muss.

Daher ist eine Anrufe (wobei b eine Instanz des Dienstes B):

b.RegisterHandler(Guid id, Action<byte[]> messageHandler); 

Um Dienst A zu testen, ich muss in der Lage sein messageHandler zu nennen, da dies der einzige Weg ist zur Zeit es akzeptiert Mitteilungen.

Kann dies mit Moq geschehen? dh. Kann ich Service B nachspielen, so dass, wenn RegisterHandler aufgerufen wird, der Wert von messageHandler zu meinem Test ausgegeben wird?

Oder muss ich das neu gestalten? Gibt es irgendwelche Entwurfsmuster, die ich in diesem Fall verwenden sollte? Kennt jemand gute Ressourcen für diese Art von Design?

Antwort

9

Sie können eine Instanz des Callback (oder einem anderen Eingabeparameter) erhalten, indem die Callback mit (der Namensähnlichkeit ist nebensächlich) Methode auf dem Mock :

[TestMethod] 
public void Test19() 
{ 
    Action<byte[]> callback = null; 

    var bSpy = new Mock<IServiceB>(); 
    bSpy.Setup(b => b.RegisterHandler(It.IsAny<Guid>(), It.IsAny<Action<byte[]>>())) 
     .Callback((Guid g, Action<byte[]> a) => callback = a); 

    var sut = new ServiceA(bSpy.Object); 
    sut.RegisterCallback(); 

    Assert.AreEqual(sut.Do, callback); 
} 

Dies funktioniert, wenn ServiceA wie folgt definiert ist:

public class ServiceA 
{ 
    private readonly IServiceB b; 

    public ServiceA(IServiceB b) 
    { 
     if (b == null) 
     { 
      throw new ArgumentNullException("b"); 
     } 

     this.b = b; 
    } 

    public void RegisterCallback() 
    { 
     this.b.RegisterHandler(Guid.NewGuid(), this.Do); 
    } 

    public void Do(byte[] bytes) 
    { 
    } 
} 
0

Ja, Sie können das Moq-Objekt so einrichten, dass es auf erwartete und unerwartete Vorgänge reagiert. Hier ist ein Visual Beispiel Test-Studio Unit ...

[TestMethod] 
public void MyTest() 
    { 
     var moqObject = new Mock<ServiceB>(); 

     // Setup the mock object to throw an exception if a certain value is passed to it... 
     moqObject.Setup(b => b.RegisterHandle(unexpectedValue).Throws(new ArgumentException()); 

     // Or, setup the mock object to expect a certain method call... 
     moqObject.Setup(b => b.RegisterHandle(expectedValue)); 

     var serviceA = new ServiceA(moqObject.Object); 

     serviceA.DoSomethingToTest(); 

     // This will throw an exception if an expected operation didn't happen... 
     moqObject.VerifyAll(); 
    }