2009-09-13 28 views
15

Esto parece algo simple pero parece que no puedo hacer que funcione.Cómo verificar que se haya utilizado otro método de la clase con Moq

Tengo una clase con un método Save que simplemente llama a otro método ShouldBeCalled(). Quiero verificar que si llamo a Save() que el otro método ShouldBeCalled() se ejecute al menos una vez. Pensé que podría hacer lo siguiente.

public class ClassA 
{ 
    public virtual void Save() 
    { 
     ShouldBeCalled(); 
    } 

    public virtual void ShouldBeCalled() 
    { 
     //This should get executed 
    } 
} 

[TestFixture] 
public class ClassA_Test 
{ 
    [Test] 
    public void Save_Should_Call_ShouldBeCalled() 
    { 
     var mockClassA = new Mock<ClassA>(); 
     mockClassA.Object.Save(); 

     mockClassA.Verify(x => x.ShouldBeCalled(), Times.AtLeastOnce()); 
    } 
} 

pero me da la excepción "invocación previstos en los simulacros al menos una vez, pero nunca se llevó a cabo: x => x.ShouldBeCalled()"

Es sólo una suposición, sino que se Moq reemplazando el El método Save() con su propia versión que ignora todo lo que tengo dentro del método Save() del objeto real.

Respuesta

28

Tiene este problema porque se está burlando de lo que está probando. Esto no tiene sentido.

Tiene razón en que Moq reemplazará la implementación de su método por la suya. El motivo es que debes usar Moq para simular que la clase está probando llamadas, no la clase que estás probando.

Esta prueba sería apropiado si su código se han diseñado de esta manera:

public class ClassA 
{ 
    BusinessLogicClass bl; 
    public ClassA(BusinessLogicClass bl) 
    { 
     this.bl = bl; 
    } 

    public void Save() 
    { 
     bl.ShouldBeCalled(); 
    } 
} 

public class BusinessLogicClass 
{ 
    public virtual void ShouldBeCalled() 
    { 
     //This should get executed 
    } 
} 

Y aquí es el criterio correcto de este método ahora:

[TestFixture] 
public class ClassA_Test 
{ 
    [Test] 
    public void Save_ShouldCallShouldBeCalled() 
    { 
     //Arrange 
     var mockBLClass = new Mock<BusinessLogicClass>(); 
     mockBLClass.Setup(x => x.ShouldBeCalled()).Verifyable(); 

     //Act  
     ClassA classA = new ClassA(mockBLClass.Object); 
     classA.Save(); 

     //Assert 
     mockBLClass.VerifyAll(); 
    } 
} 

La lección clave aquí es que simulacro/stub qué necesita ejecutar su prueba, no lo que está probando.

Espero que esto ayude, Anderson

+0

+1: ¡Excelente respuesta, completa con los ejemplos de dependencia y código! Tenga en cuenta que una alternativa al método virtual sería agregar una interfaz IBusinessLogic y pasarla. – TrueWill

+0

La interfaz de IBusinessLogic es definitivamente el camino a seguir aquí, pero no quería profundizar en ella. –

+1

Gracias por la gran respuesta. Tenía la sensación de que estaba tratando de hacer algo mal con mi enfoque y ahora sé que estaba :) – Adam

2

Sí, esto se puede hacer. Sin embargo, debe agregar una línea de código para que Moq rastree si efectivamente se llamó al método ShouldBeCalled.

Algo así como el siguiente trabajo:

var mockClassA = new Mock<ClassA>(); 
mockClassA.Setup(x => x.ShouldBeCalled()).Verifiable();  
mockClassA.Object.Save();  
mockClassA.Verify(x => s.ShouldBeCalled(), Times.AtLeastOnce()); 

El método de instalación establece expectativas. Cuando llama a Verify, le está pidiendo a Moq que verifique estas expectativas. Si no realiza una llamada de Configuración para crear expectativas para el método ShouldBeCalled, entonces Moq no considera que se pueda rastrear y, por lo tanto, fallará cuando intente verificarlo.

+0

He intentado lo anterior pero sigo teniendo el mismo error. Lo que dices tiene sentido, es por eso que no puedo entender por qué no está funcionando. – Adam

+0

¿Se puede cambiar la declaración de configuración para simular una devolución? Por ejemplo, mockClassA.Setup (x => x.ShouldBeCalled()). Devuelve (...). –

+0

La configuración es redundante aquí ya que no está utilizando Strict. Pero +1 en general –

4

intente utilizar el CallBase = true y luego falsa. Ejecuté tu código y funciona.

var mockClassA = new Mock<ClassA>(); 
mockClassA.CallBase = true; 
mockClassA.Object.Save(); 
mockClassA.CallBase = false; 
mockClassA.Verify(x => x.ShouldBeCalled(), Times.AtLeastOnce()); 
+0

Esto funciona, ¡guau! Soy nuevo en Moq, así que no tengo idea de si esto es una buena práctica o no, pero funciona. Tenga en cuenta que esto solo funciona si se burla de una clase concreta con métodos virtuales, como es el caso de ClassA. Si la clase implementa una interfaz y creas el simulacro desde la interfaz, esto no funcionará. por ejemplo, si ClassA implementara IClassA, la primera línea de la respuesta de Eric cambiaría a "var mockClassA = new Mock ();" Burlando de la interfaz de esta manera, la prueba fallará ya que nunca se llamará a ShouldBeCalled. Problema muy similar al código original en la pregunta de Adam. –

1

Puede stub métodos en el sistema bajo prueba usando CallBase.

[TestFixture] 
public class ClassA_Test 
{ 
    [Test] 
    public void Save_Should_Call_ShouldBeCalled() 
    { 
     // Arrange 
     var mockClassA = new Mock<ClassA>(); 
     mockClassA.CallBase = true; // this will call real methods unless the method is mocked/stubbed. 
     mockClassA.Setup(a => a.ShouldBeCalled()); 

     // Act 
     mockClassA.Save(); 

     // Assert 
     mockClassA.Verify(a => a.ShouldBeCalled(), Times.Once()); 
    } 
} 
+0

el hipervínculo en esta respuesta no está actualizado. – buffjape

Cuestiones relacionadas