Zombando de uma dependência com AutoFixture
-
21-12-2019 - |
Pergunta
Recentemente comecei a usar AutoFixture+AutoMoq e estou tentando criar uma instância de Func<IDbConnection>
(ou seja, uma connection factory).
var fixture = new Fixture().Customize(new AutoMoqCustomization());
var connectionFactory = fixture.Create<Func<IDbConnection>>();
Isso parece funcionar muito bem:
- Meu sistema em teste pode chamar o delegado e obterá uma simulação de
IDbConnection
- Para o qual posso ligar
CreateCommand
, o que me dará uma zombariaIDbCommand
- Para o qual posso ligar
ExecuteReader
, o que me dará uma zombariaIDataReader
Agora quero realizar configurações adicionais no mock de IDataReader
, como fazê-lo retornar true
quando Read()
é chamado.
Pelo que li, deveria estar usando Freeze
por esta:
var dataReaderMock = fixture.Freeze<Mock<IDataReader>>();
dataReaderMock.Setup(dr => dr.Read())
.Returns(true);
Isso não parece atender às minhas expectativas.Quando eu ligo IDbCommand.ExecuteReader
, obterei um leitor diferente daquele que acabei de congelar/configurar.
Aqui está um exemplo:
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());
O que estou fazendo de errado?Como consigo outros fixtures, como IDbCommand
, para usar a instância simulada de IDataReader
?
Solução
A partir da versão 3.20.0, você pode usar AutoConfiguredMoqCustomization
.Isso configurará automaticamente todos os mocks para que os valores de retorno de seus membros sejam gerados pelo AutoFixture.
Por exemplo., IDbConnetion.CreateCommand
será configurado automaticamente para retornar um IDbCommand
do aparelho, e IDbCommand.ExecuteReader
será configurado automaticamente para retornar um IDataReader
do aparelho.
Todos esses testes devem passar agora:
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());
Outras dicas
Você tem que Freeze
o Mock<IDbCommand>
também – e configure o objeto simulado (como um Stub) para retornar o existente dataReaderMock.Object
instância.
Se você adicionar o seguinte à fase de organização do seu teste, o teste será aprovado:
var dbCommandStub =
fixture
.Freeze<Mock<IDbCommand>>()
.Setup(x => x.ExecuteReader())
.Returns(dataReaderMock.Object);
Embora a solução do Nikos funcione, eu não recomendaria zombar do ado.net.
Na minha opinião, seus testes provavelmente serão difíceis de entender, manter e não lhe darão a confiança que deveriam lhe dar.
Eu consideraria testar sua camada de dados indo até o banco de dados, mesmo que seja mais lento.
Eu recomendaria a leitura deste artigo sobre as melhores práticas para zombaria:http://codebetter.com/jeremymiller/2006/01/10/best-and-worst-practices-for-mock-objects/
Não zombe dos outros:http://aspiringcraftsman.com/2012/04/01/tdd-best-practices-dont-mock-others/
Não sei sua situação exata, mas de qualquer forma gostaria de compartilhar isso.