使用 AutoFixture 模拟依赖关系
-
21-12-2019 - |
题
我最近开始使用 AutoFixture+AutoMoq 并且我正在尝试创建一个实例 Func<IDbConnection>
(即连接工厂)。
var fixture = new Fixture().Customize(new AutoMoqCustomization());
var connectionFactory = fixture.Create<Func<IDbConnection>>();
这似乎工作得相当好:
- 我的被测系统可以调用委托,它将得到一个模拟
IDbConnection
- 然后我可以打电话
CreateCommand
, ,这会让我嘲笑IDbCommand
- 然后我可以打电话
ExecuteReader
, ,这会让我嘲笑IDataReader
我现在想在模拟上执行额外的设置 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.20.0 开始,您可以使用 AutoConfiguredMoqCustomization
. 。这将自动配置所有模拟,以便其成员的返回值由 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());
其他提示
你必须 Freeze
这 Mock<IDbCommand>
以及 - 并设置模拟对象(作为存根)以返回现有的 dataReaderMock.Object
实例。
如果将以下内容添加到测试的排列阶段,测试将通过:
var dbCommandStub =
fixture
.Freeze<Mock<IDbCommand>>()
.Setup(x => x.ExecuteReader())
.Returns(dataReaderMock.Object);
虽然 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/
我不知道你的具体情况,但无论如何我想分享一下。