unit testing - verifiable - ¿Restablecer la verificación simulada en Moq?
verify moq (9)
Además de responder por @stackunderflow (jaja, buen nick :))
En versiones posteriores de Moq, Moq.MockExtensions.ResetCalls()
se marcó como obsoleto. mock.Invocations.Clear()
debe utilizar en su lugar:
foo.Invocations.Clear();
Configurar como tal:
public interface IFoo
{
void Fizz();
}
[Test]
public void A()
{
var foo = new Mock<IFoo>(MockBehavior.Loose);
foo.Object.Fizz();
foo.Verify(x => x.Fizz());
// stuff here
foo.Verify(x => x.Fizz(), Times.Never()); // currently this fails
}
Básicamente, me gustaría ingresar algún código en // stuff here
para hacer foo.Verify(x => x.Fizz(), Times.Never())
el foo.Verify(x => x.Fizz(), Times.Never())
.
Y debido a que esto probablemente constituye un abuso de prueba moq / unit, mi justificación es que puedo hacer algo como esto:
[Test]
public void Justification()
{
var foo = new Mock<IFoo>(MockBehavior.Loose);
foo.Setup(x => x.Fizz());
var objectUnderTest = new ObjectUnderTest(foo.Object);
objectUnderTest.DoStuffToPushIntoState1(); // this is various lines of code and setup
foo.Verify(x => x.Fizz());
// reset the verification here
objectUnderTest.DoStuffToPushIntoState2(); // more lines of code
foo.Verify(x => x.Fizz(), Times.Never());
}
Básicamente, tengo un objeto de estado en el que se requiere bastante trabajo (tanto en términos de hacer varios objetos de simulacro como de otros problemas) para insertarlo en State1. Entonces quiero probar la transición de State1 a State2. En lugar de duplicar o abstraer el código, prefiero solo reutilizar la prueba State1, insertarlo en State2 y realizar mis Asserts, todo lo cual puedo hacer excepto las llamadas de verificación.
Creo que mucho después de crear esta publicación, agregaron la funcionalidad que el OP había pedido, hay un método de extensión Moq llamado Moq.MockExtensions.ResetCalls () .
Con este método puedes hacer exactamente lo que deseas como se muestra a continuación:
[Test]
public void Justification()
{
var foo = new Mock<IFoo>(MockBehavior.Loose);
foo.Setup(x => x.Fizz());
var objectUnderTest = new ObjectUnderTest(foo.Object);
objectUnderTest.DoStuffToPushIntoState1(); // this is various lines of code and setup
foo.Verify(x => x.Fizz());
foo.ResetCalls(); // *** Reset the verification here with this glorious method ***
objectUnderTest.DoStuffToPushIntoState2(); // more lines of code
foo.Verify(x => x.Fizz(), Times.Never());
}
De hecho, este es el abuso de la prueba unitaria ya que está verificando dos cosas en una prueba. Su vida sería mucho más fácil si saca la prueba ObjectUnderTest
de la prueba y la ObjectUnderTest
en un método de configuración común. Entonces sus pruebas se vuelven mucho más legibles e independientes entre sí.
Más que código de producción, el código de prueba debe optimizarse para su legibilidad y aislamiento. Una prueba para un aspecto del comportamiento del sistema no debe afectar otros aspectos. Realmente es mucho más fácil refactorizar código común en un método de configuración que intentar restablecer los objetos simulados.
ObjectUnderTest _objectUnderTest;
[Setup] //Gets run before each test
public void Setup() {
var foo = new Mock<IFoo>(); //moq by default creates loose mocks
_objectUnderTest = new ObjectUnderTest(foo.Object);
}
[Test]
public void DoStuffToPushIntoState1ShouldCallFizz() {
_objectUnderTest.DoStuffToPushIntoState1(); // this is various lines of code and setup
foo.Verify(x => x.Fizz());
}
[Test]
public void DoStuffToPushIntoState2ShouldntCallFizz() {
{
objectUnderTest.DoStuffToPushIntoState2(); // more lines of code
foo.Verify(x => x.Fizz(), Times.Never());
}
Depende de la versión de Mock que uses, sé con certeza que podemos hacer esto
someMockObject.ResetCalls ();
El siguiente enfoque funciona bien para mí (usando Moq.Sequence)
public void Justification()
{
var foo = new Mock<IFoo>(MockBehavior.Loose);
foo.Setup(x => x.Fizz());
var objectUnderTest = new ObjectUnderTest(foo.Object);
objectUnderTest.DoStuffToPushIntoState1(); // this is various lines of code and setup
foo.Verify(x => x.Fizz());
// Some cool stuff
using (Sequence.Create())
{
foo.Setup(x => x.Fizz()).InSequence(Times.Never())
objectUnderTest.DoStuffToPushIntoState2(); // more lines of code
}
}
Avísame si te funcionó
En caso de que las personas MOQ estén escuchando, tengo un caso en el que también debo restablecer la verificación. Tengo alrededor de 200 pruebas en una clase de prueba NUnit. El SUT es bastante complicado de configurar (necesitamos 6 simulacros y algún otro código de configuración). En todo nuestro conjunto de pruebas, tenemos aproximadamente 7000 pruebas y estamos teniendo problemas de memoria, así que estoy tratando de eliminar los requisitos de memoria. Quiero mover todas las llamadas simuladas a TestFixtureSetUp en lugar de SetUp. Esto funcionó excepto en los pocos casos que verifican si se llamó un método exactamente una vez. La segunda prueba que verificó Times.Exactamente (1) ahora falla. Si pudiera restablecer la verificación, podría reutilizar la simulación y usarla mucho más sabiamente.
Sé que podría mover la configuración a un método diferente. Por favor, no me castigues por eso. Solo estoy presentando un caso válido en el que poder restablecer la verificación sería útil.
No creo que puedas restablecer un simulacro como este. En cambio, si sabes que debes llamar a Fizz
una vez cuando pasas al estado 1, puedes hacer tu verificación así:
objectUnderTest.DoStuffToPushIntoState1();
foo.Verify(x => x.Fizz(), Times.Once()); // or however many times you expect it to be called
objectUnderTest.DoStuffToPushIntoState2();
foo.Verify(x => x.Fizz(), Times.Once());
Habiendo dicho eso, aún crearía dos pruebas separadas para esto. Como dos pruebas, es más fácil ver si la transición al estado 1 está fallando, o la transición al estado 2 está fallando. Además, cuando se prueba de esta manera, si su transición al estado 1 falla, el método de prueba finaliza y su transición al estado 2 no se prueba.
Editar
Como ejemplo de esto, probé el siguiente código con xUnit:
[Fact]
public void Test()
{
var foo = new Mock<IFoo>(MockBehavior.Loose);
foo.Object.Fizz();
foo.Verify(x => x.Fizz(), Times.Once(), "Failed After State 1");
// stuff here
foo.Object.Fizz();
foo.Verify(x => x.Fizz(), Times.Once(), "Failed after State 2");
}
Esta prueba falla con el mensaje "Falló después del estado 2". Esto simula lo que sucedería si su método que empuja a foo hacia el estado 2 llama a Fizz
. Si lo hace, la segunda Verify
fallará.
Mirando su código nuevamente, ya que está llamando a un método para verificar si llama / no llama a otro método en el simulacro, creo que necesita establecer CallBase
en true
para que se llame a DoStuffToPushIntoState2
base en lugar de anular el simulacro.
Puede usar el método de Devolución de llamada en lugar de Verificar y contar las llamadas.
Esto se demuestra en la página de inicio rápido de Moq , por lo tanto:
// returning different values on each invocation
var mock = new Mock<IFoo>();
var calls = 0;
mock.Setup(foo => foo.GetCountThing())
.Returns(() => calls)
.Callback(() => calls++);
// returns 0 on first invocation, 1 on the next, and so on
Console.WriteLine(mock.Object.GetCountThing());
También he sido testigo del Times. Exactamente (1) error de verificación en pruebas unitarias que usan MoQ, con un mensaje de error "se llamó 2 veces". Veo esto como un error en MoQ, ya que esperaría estados de prueba limpios en cada ejecución de prueba.
Mi trabajo fue asignar una nueva instancia simulada y un objetivo de prueba en la configuración de prueba.
private Mock<IEntityMapper> entityMapperMock;
private OverdraftReportMapper target;
[SetUp]
public void TestSetUp()
{
entityMapperMock = new Mock<IEntityMapper>();
target = new OverdraftReportMapper(entityMapperMock.Object);
}