如何使用 AutoFixture AutoMoq 将测试参数中定义的模拟类型自动注入 SUT
How to auto inject mocked types defined in test parameters into SUT using AutoFixture AutoMoq
假设我有以下代码示例:
public interface IBar
{
string GetMessage();
}
public class Foo
{
private readonly IBar bar;
public Foo(IBar bar)
{
this.bar = bar;
}
public string Prefix { get; set; }
public string RetrieveMessage()
{
if (string.IsNullOrWhiteSpace(Prefix)) return string.Empty;
return $"{Prefix} {this.bar.GetMessage()}";
}
}
我目前有一个如下所示的测试:
[Test, AutoMoqData]
public void TestThatIHaveToManuallyCreateSUT(Mock<IBar> mockBar)
{
// Arrange
mockBar.Setup(x => x.GetMessage()).Returns("World");
Foo sut = new Foo(mockBar.Object)
{
Prefix = "Hello",
};
// Act
var actual = sut.RetrieveMessage();
// Assert
Assert.AreEqual($"{sut.Prefix} World", actual);
mockBar.Verify(x => x.GetMessage(), Times.Once());
}
工作正常,但存在需要在测试中手动创建 SUT 的问题。当我的代码依赖于需要在其中包含一些数据的属性,并且需要始终记住用某个值初始化 属性(就像 Foo.Prefix
的情况一样)时,这就成了一个问题。 =18=]
理想情况下,我希望能够按以下方式定义测试:
[Test, AutoMoqData]
public void TestWhereAutoDataCreatesTheSUT(Mock<IBar> mockBar, Foo sut)
{
// Arrange
mockBar.Setup(x => x.GetMessage()).Returns("World");
// Act
var actual = sut.RetrieveMessage();
// Assert
Assert.AreEqual($"{sut.Prefix} World", actual);
mockBar.Verify(x => x.GetMessage(), Times.Once());
}
其中 SUT 是使用 mockBar
实例自动创建的,然后我可以在测试中进一步使用它。
上面的代码不起作用,因为它会自动模拟一个新实例 IBar
,并且不给我访问底层模拟对象的权限。
我的 AutoMoqDataAttribute
:
没有任何花哨的东西
public class AutoMoqDataAttribute : AutoDataAttribute
{
public AutoMoqDataAttribute() : base(() => new Fixture().Customize(new AutoMoqCustomization() { ConfigureMembers = true }))
{
}
}
但我想如果有任何设置需要进行以实现此目的,它会在这里。
有没有办法使用 AutoFixture AutoMoq 将测试参数中定义的模拟类型自动注入到 SUT 中?
基于 following article 我建议重构你的测试
[Test, AutoMoqData]
public void TestWhereAutoDataCreatesTheSUT([Frozen]IBar bar, Foo sut) {
// Arrange
Mock<IBar> mockBar = Mock.Get(bar);
mockBar.Setup(x => x.GetMessage()).Returns("World");
// Act
var actual = sut.RetrieveMessage();
// Assert
Assert.AreEqual($"{sut.Prefix} World", actual);
mockBar.Verify(x => x.GetMessage(), Times.Once());
}
假设我有以下代码示例:
public interface IBar
{
string GetMessage();
}
public class Foo
{
private readonly IBar bar;
public Foo(IBar bar)
{
this.bar = bar;
}
public string Prefix { get; set; }
public string RetrieveMessage()
{
if (string.IsNullOrWhiteSpace(Prefix)) return string.Empty;
return $"{Prefix} {this.bar.GetMessage()}";
}
}
我目前有一个如下所示的测试:
[Test, AutoMoqData]
public void TestThatIHaveToManuallyCreateSUT(Mock<IBar> mockBar)
{
// Arrange
mockBar.Setup(x => x.GetMessage()).Returns("World");
Foo sut = new Foo(mockBar.Object)
{
Prefix = "Hello",
};
// Act
var actual = sut.RetrieveMessage();
// Assert
Assert.AreEqual($"{sut.Prefix} World", actual);
mockBar.Verify(x => x.GetMessage(), Times.Once());
}
工作正常,但存在需要在测试中手动创建 SUT 的问题。当我的代码依赖于需要在其中包含一些数据的属性,并且需要始终记住用某个值初始化 属性(就像 Foo.Prefix
的情况一样)时,这就成了一个问题。 =18=]
理想情况下,我希望能够按以下方式定义测试:
[Test, AutoMoqData]
public void TestWhereAutoDataCreatesTheSUT(Mock<IBar> mockBar, Foo sut)
{
// Arrange
mockBar.Setup(x => x.GetMessage()).Returns("World");
// Act
var actual = sut.RetrieveMessage();
// Assert
Assert.AreEqual($"{sut.Prefix} World", actual);
mockBar.Verify(x => x.GetMessage(), Times.Once());
}
其中 SUT 是使用 mockBar
实例自动创建的,然后我可以在测试中进一步使用它。
上面的代码不起作用,因为它会自动模拟一个新实例 IBar
,并且不给我访问底层模拟对象的权限。
我的 AutoMoqDataAttribute
:
public class AutoMoqDataAttribute : AutoDataAttribute
{
public AutoMoqDataAttribute() : base(() => new Fixture().Customize(new AutoMoqCustomization() { ConfigureMembers = true }))
{
}
}
但我想如果有任何设置需要进行以实现此目的,它会在这里。
有没有办法使用 AutoFixture AutoMoq 将测试参数中定义的模拟类型自动注入到 SUT 中?
基于 following article 我建议重构你的测试
[Test, AutoMoqData]
public void TestWhereAutoDataCreatesTheSUT([Frozen]IBar bar, Foo sut) {
// Arrange
Mock<IBar> mockBar = Mock.Get(bar);
mockBar.Setup(x => x.GetMessage()).Returns("World");
// Act
var actual = sut.RetrieveMessage();
// Assert
Assert.AreEqual($"{sut.Prefix} World", actual);
mockBar.Verify(x => x.GetMessage(), Times.Once());
}