Burlándose de una dependencia con AutoFixture
-
21-12-2019 - |
Pregunta
Recientemente comencé a usar AutoFixture+AutoMoq y estoy intentando crear una instancia de Func<IDbConnection>
(es decir, una fábrica de conexiones).
var fixture = new Fixture().Customize(new AutoMoqCustomization());
var connectionFactory = fixture.Create<Func<IDbConnection>>();
Esto parece funcionar bastante bien:
- Mi sistema bajo prueba puede llamar al delegado y se burlará de él.
IDbConnection
- al que luego puedo llamar
CreateCommand
, lo que me hará burlarse deIDbCommand
- al que luego puedo llamar
ExecuteReader
, lo que me hará burlarse deIDataReader
Ahora quiero realizar configuraciones adicionales simulando IDataReader
, como hacer que regrese true
cuando Read()
se llama.
Por lo que he leído, debería usar Freeze
para esto:
var dataReaderMock = fixture.Freeze<Mock<IDataReader>>();
dataReaderMock.Setup(dr => dr.Read())
.Returns(true);
Aunque esto no parece cumplir mis expectativas.cuando llamo IDbCommand.ExecuteReader
, obtendré un lector diferente al que acabo de congelar/configurar.
He aquí un ejemplo:
var fixture = new Fixture().Customize(new AutoMoqCustomization());
var dataReaderMock = fixture.Freeze<Mock<IDataReader>>();
dataReaderMock.Setup(dr => dr.Read())
.Returns(true);
//true - Create<IDataReader> retrieves the data reader I just mocked
Assert.AreSame(dataReaderMock.Object, fixture.Create<IDataReader>());
//false - IDbCommand returns a different instance of IDataReader
Assert.AreSame(dataReaderMock.Object, fixture.Create<IDbCommand>().ExecuteReader());
¿Qué estoy haciendo mal?¿Cómo consigo otros accesorios, como IDbCommand
, para usar la instancia simulada de IDataReader
?
Solución
A partir de 3.20.0, puedes usar AutoConfiguredMoqCustomization
.Esto configurará automáticamente todos los simulacros para que AutoFixture genere los valores de retorno de sus miembros.
P.ej., IDbConnetion.CreateCommand
se configurará automáticamente para devolver un IDbCommand
del aparato, y IDbCommand.ExecuteReader
se configurará automáticamente para devolver un IDataReader
del aparato.
Todas estas pruebas deberían pasar ahora:
var fixture = new Fixture().Customize(new AutoConfiguredMoqCustomization());
var dataReaderMock = fixture.Freeze<Mock<IDataReader>>();
dataReaderMock.Setup(dr => dr.Read())
.Returns(true);
//all pass
Assert.Same(dataReaderMock.Object, fixture.Create<IDataReader>());
Assert.Same(dataReaderMock.Object, fixture.Create<IDbCommand>().ExecuteReader());
Assert.Same(dataReaderMock.Object, fixture.Create<IDbConnection>().CreateCommand().ExecuteReader());
Assert.Same(dataReaderMock.Object, fixture.Create<Func<IDbConnection>>()().CreateCommand().ExecuteReader());
Otros consejos
Tienes que Freeze
el Mock<IDbCommand>
también – y configurar el objeto simulado (como un Stub) para devolver el existente dataReaderMock.Object
instancia.
Si agrega lo siguiente a la fase Organizar de su prueba, la prueba pasará:
var dbCommandStub =
fixture
.Freeze<Mock<IDbCommand>>()
.Setup(x => x.ExecuteReader())
.Returns(dataReaderMock.Object);
Mientras que la solución de Nikos funciona, no lo recomendaría a burlarse de ado.net.
En mi opinión, sus pruebas probablemente serán difíciles de entender, mantener y no le darán la confianza que sus pruebas deberían darle.
Consideraría probar su capa de datos al pasar todo el camino a la base de datos a pesar de que es más lento.
Recomendaría leer este artículo con respecto a las mejores prácticas para burlarse: http://codebetter.com/Jeremymiller / 2006/01/10 / Best-Peor-Peor-Peor-Pretty-for-Mock-Objects /
No te burles de otros: http://aspiringcraftsman.com/2012/04/01 / TDD-BEST-PRÁCTICAS-DONT-MOFE-OTROS /
No sé su situación exacta, pero de todos modos quería compartir esto.