Beffare una dipendenza con autofixture
-
21-12-2019 - |
Domanda
Ho recentemente iniziato a utilizzare AutoFixture + AutomoQ e sto provando a creare un'istanza di Func<IDbConnection>
(I.e., una fabbrica di connessione).
var fixture = new Fixture().Customize(new AutoMoqCustomization());
var connectionFactory = fixture.Create<Func<IDbConnection>>();
.
Questo sembra funzionare piuttosto bene:
- .
- Il mio sistema sotto test può chiamare il delegato e riceverà un finto di
IDbConnection
- su cui posso chiamare
CreateCommand
, che mi prenderà una presa diIDbCommand
- su cui posso chiamare
ExecuteReader
, che mi prenderà una presa diIDataReader
Voglio ora eseguire ulteriori configurazioni sulla finzione di IDataReader
, ad esempio renderlo restituito true
quando viene chiamato Read()
.
Da quello che ho letto, dovrei usare Freeze
per questo:
var dataReaderMock = fixture.Freeze<Mock<IDataReader>>();
dataReaderMock.Setup(dr => dr.Read())
.Returns(true);
.
Questo non sembra di soddisfare le mie aspettative però. Quando chiamo IDbCommand.ExecuteReader
, riceverò un lettore diverso da quello che ho appena congelato / configurazione.
Ecco un esempio:
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());
.
Cosa sto facendo male? Come posso ottenere altri dispositivi, come IDbCommand
, per utilizzare l'istanza deriso di IDataReader
?
Soluzione
A partire da 3.20.0, è possibile utilizzare AutoConfiguredMoqCustomization
.Questo configurerà automaticamente tutti i guasti in modo che i valori di ritorno dei membri siano generati da AutoFixture.
E.G., IDbConnetion.CreateCommand
verrà automaticamente configurato per restituire un IDbCommand
dal dispositivo, e IDbCommand.ExecuteReader
verrà automaticamente configurato per restituire un IDataReader
dal dispositivo.
Tutti questi test dovrebbero passare ora:
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());
. Altri suggerimenti
Devi anche generarecodicitagcode anche il Freeze
e configurano l'oggetto finto (come stub) per restituire l'istanza Mock<IDbCommand>
esistente.
Se si aggiunge quanto segue alla fase dell'oggetto del test, il test passerà:
var dbCommandStub =
fixture
.Freeze<Mock<IDbCommand>>()
.Setup(x => x.ExecuteReader())
.Returns(dataReaderMock.Object);
. Mentre la soluzione da Nikos funziona, non vorrei non raccomandare ADO.net.
Secondo me, i tuoi test saranno probabilmente difficili da capire, mantenere e non ti darà la fiducia che i tuoi test dovrebbero darti.
Considererei il test del tuo livello di dati andando fino al database anche se è più lento.
Consiglierei di leggere questo articolo riguardante le migliori pratiche per il beffardo: http://codebetter.com/jeremymymiller / 2006/01/10 / Best-and-peggiore-pratiche-Practice-per-mock-oggetti /
Non prendere in giro gli altri: http://aspiringcraftsman.com/2012/04/01 / TDD-Best-Practices-DONT-DROCK-Altri /
Non conosco la tua situazione esatta ma comunque volevo condividere questo.