Verwenden von Moq, um festzustellen, ob eine Methode aufgerufen wird

159

Nach meinem Verständnis kann ich testen, ob ein Methodenaufruf erfolgt, wenn ich eine übergeordnete Methode aufrufe, dh:

public abstract class SomeClass()
{    
    public void SomeMehod()
    {
        SomeOtherMethod();
    }

    internal abstract void SomeOtherMethod();
}

Ich möchte das testen, wenn ich anrufe, SomeMethod()dann erwarte ich, dass SomeOtherMethod()das angerufen wird.

Habe ich Recht, wenn ich denke, dass diese Art von Test in einem spöttischen Rahmen verfügbar ist?

Owen
quelle

Antworten:

186

Mit Verify können Sie feststellen, ob eine Methode in etwas, das Sie verspottet haben, aufgerufen wurde, z.

static void Main(string[] args)
{
        Mock<ITest> mock = new Mock<ITest>();

        ClassBeingTested testedClass = new ClassBeingTested();
        testedClass.WorkMethod(mock.Object);

        mock.Verify(m => m.MethodToCheckIfCalled());
}

class ClassBeingTested
{
    public void WorkMethod(ITest test)
    {
        //test.MethodToCheckIfCalled();
    }
}

public interface ITest
{
    void MethodToCheckIfCalled();
}

Wenn die Zeile kommentiert bleibt, wird beim Aufruf von Verify eine MockException ausgelöst. Wenn es nicht kommentiert ist, wird es passieren.

Paul
quelle
7
Dies ist die richtige Antwort. Sie müssen jedoch etwas verstehen. Sie können eine Methode / Eigenschaft, die nicht abstrakt oder virtuell ist, NICHT verspotten (offensichtlich können alle Schnittstellenmethoden und -eigenschaften verspottet werden).
25
-1: Der .Expect (...). Verifizierbare () ist in diesem Code redundant. Mit AAA ist die Überprüfung, die Sie haben, genau richtig. .Verifizierbar ist zur Verwendung mit .Verify () i, .e. die no arg version. Siehe stackoverflow.com/questions/980554/…
Ruben Bartelink
@ I-- ja es kann
reggaeguitar
6

Nein, bei Mock-Tests wird davon ausgegangen, dass Sie bestimmte testbare Entwurfsmuster verwenden, von denen eines die Injektion ist. In Ihrem Fall würden Sie testen SomeClass.SomeMethod und SomeOtherMethodmüssen in einer anderen Entität implementiert werden, die verbunden werden muss.

Ihr SomeclassKonstruktor würde so aussehen New(ISomeOtherClass). Dann würden Sie die ISomeOtherClassErwartung verspotten und festlegen SomeOtherMethod, dass sie aufgerufen werden soll, und die Erwartung überprüfen.

Val
quelle
0

Obwohl ich damit einverstanden bin, dass die Antwort von @ Paul der empfohlene Weg ist, möchte ich nur einen alternativen Weg hinzufügen, der von moqaußerhalb des Selbst bereitgestellt wird .

Da SomeClassist abstractes zwar spöttisch, aber public void SomeMehod()nicht. Der Punkt ist, den Weg zu finden, um diese Methode zu verspotten und irgendwie aufzurufen und dann CallBaseden Aufruf an das weiterzuleiten SomeOtherMethod(). Es mag wie ein Hack klingen, ist aber im Wesentlichen einfach. Es könnte in dem Fall verwendet werden, wenn das vorgeschlagene Refactoring nicht möglich ist.

// This class is used only for test and purpose is make SomeMethod mockable
public abstract class DummyClass : SomeClass
{
    public virtual void DummyMethod() => base.SomeMethod();
}

Dann können Sie festlegen DummyMethod(), dass der Anruf durch Setzen des CallBaseFlags weitergegeben wird.

//Arrange
var mock = new Mock<DummyClass>();
mock.Setup(m => m.DummyMethod()).CallBase();

//Act
mock.Object.SomeMethod();

//Assert
mock.Verify(m => m.SomeOtherMethod(), Times.Once);
Johnny
quelle
Downvoted, weil es komplizierter ist und eine Boilerplate DummyClass erfordert
reggaeguitar
upvoted, weil Sie manchmal nicht refactor können und Sie die Implementierung so wie sie ist testen müssen
wickdninja