5 Stimmen

Moq und Zugriff auf aufgerufene Parameter

Ich habe gerade begonnen, Unit-Tests (mit xUnit und Moq) auf einem bereits etablierten Projekt von mir zu implementieren. Das Projekt verwendet ausgiebig Abhängigkeit Injektion über den Unity-Container.

Ich habe zwei Dienste A und B. Dienst A ist derjenige, der in diesem Fall getestet wird. Dienst A ruft B auf und übergibt ihm einen Delegaten an eine interne Funktion. Dieser "Rückruf" wird verwendet, um A zu benachrichtigen, wenn eine Nachricht eingegangen ist, die er bearbeiten muss.

Daher ruft A an (wobei b eine Instanz des Dienstes B ist):

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

Um Dienst A zu testen, muss ich in der Lage sein, Folgendes aufzurufen messageHandler da dies derzeit die einzige Möglichkeit ist, Nachrichten zu empfangen.

Kann dies mit Moq geschehen, d.h. kann ich den Dienst B nachbilden, so dass, wenn RegisterHandler aufgerufen wird, wird der Wert von messageHandler zu meinem Test weitergegeben wird?

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

9voto

Mark Seemann Punkte 216836

Sie können eine Instanz des Callbacks (oder jeden anderen Eingabeparameter) erhalten, indem Sie den Callback ( die Namensähnlichkeit ist zufällig ) 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)
    {
    }
}

0voto

Chris Arnold Punkte 5703

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

[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();
    }

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X