使用AutoFixture模拟依赖项

时间:2014-07-31 09:42:40

标签: c# unit-testing moq autofixture automoq

我最近开始使用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
  4. 我现在想要在模拟IDataReader时执行其他设置,例如在调用true时让它返回Read()

    根据我的阅读,我应该使用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());
    

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

3 个答案:

答案 0 :(得分:5)

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

例如,IDbConnetion.CreateCommand将自动配置为从灯具返回IDbCommandIDbCommand.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());

答案 1 :(得分:3)

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

如果您将以下内容添加到测试的编配阶段,测试将通过:

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

答案 2 :(得分:1)

虽然Nikos的解决方案有效,但我不建议嘲笑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/

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