使用 AutoMoq 属性模拟构造函数依赖
Mocking constructor dependencies using AutoMoq attribute
我想知道在使用设置 AutoData
的测试用例时,是否有一种方法可以在调用被测系统 (SUT) 的构造函数之前为依赖项设置模拟。 =20=]
我的 SUT 看起来像:
class Sut
{
private readonly IFoo foo;
public Sut(IFooFactory factory)
{
this.foo = factory.Build(1, 2);
}
public IFoo Foo
{
get
{
return this.foo;
}
}
}
所以我正在编写的测试看起来像:
[Theory]
[AutoData]
internal void Foo_IsCorrectlySet_Test(
[Frozen] Mock<IFooFactory> fooFactory,
IFoo foo,
Sut sut)
{
fooFactory.Setup(mock => mock.Build(1, 2))
.Returns(foo)
.Verifiable();
var actual = sut.Foo;
Assert.Equal(foo, sut);
fooFactory.Verify();
}
显然,在我能够设置 IFooFactory
之前,此测试作为 Sut
运行 的构造函数失败了。所以我想我可能已经能够在测试中将 Sut
的声明更改为 Lazy<Sut>
。
但是在实际测试代码是 运行 之前,构造函数仍然是 运行,这意味着我的测试将失败。
现在我知道我可以使用实际的 Fixture
对象轻松设置此测试并手动创建我的所有对象并在我调用创建 Sut
之前设置它们,这很好但是我想让我的测试大致相同,因此我想知道是否有一种方法可以让我仍然可以使用 AutoData
属性而不是 运行 构造函数来设置我的测试,直到一切都完成设置?
可以使用Fixture object pattern。
这允许您在创建 sut
.
之前设置模拟
行中的内容:
private class Fixture
{
public Mock<FooFactory> FooFactoryMock { get; set; } = new Mock<FooFactory>();
public Sut GetSut()
{
return new Sut(FooFactoryMock.Object);
}
}
[Theory]
[AutoData]
internal void Foo_IsCorrectlySet_Test(
IFoo foo)
{
var fixture = new Fixture();
fixture.FooFactory.Setup(mock => mock.Build(1, 2))
.Returns(foo)
.Verifiable();
var sut = fixture.GetSut();
var actual = sut.Foo;
Assert.Equal(foo, sut);
fooFactory.Verify();
}
AutoFixture 最初是作为测试驱动开发 (TDD) 的工具构建的,而 TDD 就是关于 反馈 的。本着 GOOS 的精神,您应该 听取您的测试 。如果测试很难写,你应该考虑你的 API 设计。 AutoFixture 倾向于放大这种反馈,这里也可能是这种情况。
考虑 Sut
class 的不变量。由于它有一个只读的 IFoo
class 字段,我将其解释为一个强烈的迹象表明 IFoo
是 class.
的依赖项
如果是这样,则通过构造函数注入 IFoo
,而不是 IFooFactory
:
public class Sut
{
private readonly IFoo foo;
public Sut(IFoo foo)
{
this.foo = foo;
}
public IFoo Foo
{
get { return this.foo; }
}
}
您仍然可以在应用程序的 Composition Root:
中使用 IFooFactory
编写 它
var sut = new Sut(aFactory.Build(1, 2));
这将使测试更容易编写。我什至无法向您展示上述测试在重构后的效果,因为它是多余的并且可以(并且应该)删除。
FWIW,上面提出的原始设计违反了 Nikola Malovic 的 IoC 第四定律 constructors should do no work。
我想知道在使用设置 AutoData
的测试用例时,是否有一种方法可以在调用被测系统 (SUT) 的构造函数之前为依赖项设置模拟。 =20=]
我的 SUT 看起来像:
class Sut
{
private readonly IFoo foo;
public Sut(IFooFactory factory)
{
this.foo = factory.Build(1, 2);
}
public IFoo Foo
{
get
{
return this.foo;
}
}
}
所以我正在编写的测试看起来像:
[Theory]
[AutoData]
internal void Foo_IsCorrectlySet_Test(
[Frozen] Mock<IFooFactory> fooFactory,
IFoo foo,
Sut sut)
{
fooFactory.Setup(mock => mock.Build(1, 2))
.Returns(foo)
.Verifiable();
var actual = sut.Foo;
Assert.Equal(foo, sut);
fooFactory.Verify();
}
显然,在我能够设置 IFooFactory
之前,此测试作为 Sut
运行 的构造函数失败了。所以我想我可能已经能够在测试中将 Sut
的声明更改为 Lazy<Sut>
。
但是在实际测试代码是 运行 之前,构造函数仍然是 运行,这意味着我的测试将失败。
现在我知道我可以使用实际的 Fixture
对象轻松设置此测试并手动创建我的所有对象并在我调用创建 Sut
之前设置它们,这很好但是我想让我的测试大致相同,因此我想知道是否有一种方法可以让我仍然可以使用 AutoData
属性而不是 运行 构造函数来设置我的测试,直到一切都完成设置?
可以使用Fixture object pattern。
这允许您在创建 sut
.
行中的内容:
private class Fixture
{
public Mock<FooFactory> FooFactoryMock { get; set; } = new Mock<FooFactory>();
public Sut GetSut()
{
return new Sut(FooFactoryMock.Object);
}
}
[Theory]
[AutoData]
internal void Foo_IsCorrectlySet_Test(
IFoo foo)
{
var fixture = new Fixture();
fixture.FooFactory.Setup(mock => mock.Build(1, 2))
.Returns(foo)
.Verifiable();
var sut = fixture.GetSut();
var actual = sut.Foo;
Assert.Equal(foo, sut);
fooFactory.Verify();
}
AutoFixture 最初是作为测试驱动开发 (TDD) 的工具构建的,而 TDD 就是关于 反馈 的。本着 GOOS 的精神,您应该 听取您的测试 。如果测试很难写,你应该考虑你的 API 设计。 AutoFixture 倾向于放大这种反馈,这里也可能是这种情况。
考虑 Sut
class 的不变量。由于它有一个只读的 IFoo
class 字段,我将其解释为一个强烈的迹象表明 IFoo
是 class.
如果是这样,则通过构造函数注入 IFoo
,而不是 IFooFactory
:
public class Sut
{
private readonly IFoo foo;
public Sut(IFoo foo)
{
this.foo = foo;
}
public IFoo Foo
{
get { return this.foo; }
}
}
您仍然可以在应用程序的 Composition Root:
中使用IFooFactory
编写 它
var sut = new Sut(aFactory.Build(1, 2));
这将使测试更容易编写。我什至无法向您展示上述测试在重构后的效果,因为它是多余的并且可以(并且应该)删除。
FWIW,上面提出的原始设计违反了 Nikola Malovic 的 IoC 第四定律 constructors should do no work。