Eine Abhängigkeit mit AutoFixture verspotten
-
21-12-2019 - |
Frage
Ich habe vor kurzem damit begonnen, AutoFixture+AutoMoq zu verwenden und versuche, eine Instanz davon zu erstellen Func<IDbConnection>
(d. h. eine Verbindungsfabrik).
var fixture = new Fixture().Customize(new AutoMoqCustomization());
var connectionFactory = fixture.Create<Func<IDbConnection>>();
Das scheint ziemlich gut zu funktionieren:
- Mein zu testendes System kann den Delegaten aufrufen und erhält einen Schein davon
IDbConnection
- Auf dem kann ich dann anrufen
CreateCommand
, was mir einen Eindruck davon verschaffen wirdIDbCommand
- Auf dem kann ich dann anrufen
ExecuteReader
, was mir einen Eindruck davon verschaffen wirdIDataReader
Ich möchte jetzt zusätzliche Setups am Modell durchführen IDataReader
, wie zum Beispiel, dass es zurückkommt true
Wann Read()
wird genannt.
Nach dem, was ich gelesen habe, sollte ich es verwenden Freeze
dafür:
var dataReaderMock = fixture.Freeze<Mock<IDataReader>>();
dataReaderMock.Setup(dr => dr.Read())
.Returns(true);
Dies scheint jedoch nicht meinen Erwartungen zu entsprechen.Wenn ich anrufe IDbCommand.ExecuteReader
, bekomme ich einen anderen Reader als den, den ich gerade eingefroren/eingerichtet habe.
Hier ist ein Beispiel:
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());
Was mache ich falsch?Wie bekomme ich andere Vorrichtungen, wie z IDbCommand
, um die simulierte Instanz von zu verwenden IDataReader
?
Lösung
Ab 3.20.0 können Sie verwenden AutoConfiguredMoqCustomization
.Dadurch werden alle Mocks automatisch so konfiguriert, dass die Rückgabewerte ihrer Mitglieder von AutoFixture generiert werden.
Z.B., IDbConnetion.CreateCommand
wird automatisch so konfiguriert, dass ein zurückgegeben wird IDbCommand
von der Vorrichtung und IDbCommand.ExecuteReader
wird automatisch so konfiguriert, dass ein zurückgegeben wird IDataReader
aus der Vorrichtung.
Alle diese Tests sollten jetzt bestanden werden:
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());
Andere Tipps
Du musst Freeze
Die Mock<IDbCommand>
auch – und richten Sie das Scheinobjekt (als Stub) ein, um das Vorhandene zurückzugeben dataReaderMock.Object
Beispiel.
Wenn Sie der Phase „Anordnen“ Ihres Tests Folgendes hinzufügen, wird der Test bestanden:
var dbCommandStub =
fixture
.Freeze<Mock<IDbCommand>>()
.Setup(x => x.ExecuteReader())
.Returns(dataReaderMock.Object);
Obwohl die Lösung von Nikos funktioniert, würde ich nicht empfehlen, sich über ado.net lustig zu machen.
Meiner Meinung nach werden Ihre Tests wahrscheinlich schwer zu verstehen und zu warten sein und Ihnen nicht das Vertrauen geben, das Ihre Tests Ihnen geben sollten.
Ich würde in Betracht ziehen, Ihre Datenschicht zu testen, indem Sie bis zur Datenbank vorgehen, auch wenn diese langsamer ist.
Ich würde empfehlen, diesen Artikel über Best Practices zum Spotten zu lesen:http://codebetter.com/jeremymiller/2006/01/10/best-and-worst-practices-for-mock-objects/
Machen Sie sich nicht über andere lustig:http://aspiringcraftsman.com/2012/04/01/tdd-best-practices-dont-mock-others/
Ich kenne Ihre genaue Situation nicht, aber ich wollte Ihnen das trotzdem mitteilen.