用AutoFixture模拟依赖项

本文关键字:依赖 模拟 AutoFixture | 更新日期: 2023-09-27 18:15:42

我最近开始使用AutoFixture+AutoMoq,我试图创建一个Func<IDbConnection>的实例(即连接工厂)。

var fixture = new Fixture().Customize(new AutoMoqCustomization());
var connectionFactory = fixture.Create<Func<IDbConnection>>();

这似乎工作得相当好:

  1. 我的被测系统可以调用委托,它将获得IDbConnection的模拟
  2. 然后我可以调用CreateCommand,这将获得IDbCommand的模拟
  3. 然后我可以调用ExecuteReader,这将获得IDataReader的模拟

我现在想在IDataReader的模拟上执行额外的设置,例如在调用Read()时使其返回true

从我所读到的,我应该使用Freeze:

var dataReaderMock = fixture.Freeze<Mock<IDataReader>>();
dataReaderMock.Setup(dr => dr.Read())
                      .Returns(true);

这似乎不符合我的期望。当我调用IDbCommand.ExecuteReader时,我将得到一个不同于我刚刚冻结/设置的阅读器。

下面是一个例子:

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());

我做错了什么?如何让其他fixture(如IDbCommand)使用IDataReader的模拟实例?

用AutoFixture模拟依赖项

从3.20.0开始,您可以使用AutoConfiguredMoqCustomization。这将自动配置所有mock,使其成员的返回值由AutoFixture生成。

。, IDbConnetion.CreateCommand将被自动配置为从夹具返回IDbCommand, IDbCommand.ExecuteReader将被自动配置为从夹具返回IDataReader

现在所有这些测试都应该通过了:

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());

您还必须对Mock<IDbCommand>进行Freeze,并设置模拟对象(作为存根)以返回现有的dataReaderMock.Object实例。

如果在测试的Arrange阶段添加以下内容,测试将通过:

var dbCommandStub = 
    fixture
        .Freeze<Mock<IDbCommand>>()
        .Setup(x => x.ExecuteReader())
        .Returns(dataReaderMock.Object);

虽然Nikos的解决方案可以工作,但我不建议mock ado.net。

在我看来,你的测试可能很难理解,维护,不会给你信心,你的测试应该给你。

我会考虑通过一直到数据库来测试你的数据层,即使它更慢。

我推荐阅读这篇关于嘲讽的最佳实践的文章:http://codebetter.com/jeremymiller/2006/01/10/best-and-worst-practices-for-mock-objects/

不要嘲笑别人:http://aspiringcraftsman.com/2012/04/01/tdd-best-practices-dont-mock-others/

我不知道你的确切情况,但无论如何我想分享这个