gpt4 book ai didi

dependency-injection - 为什么带有 AutoMoqCustomization 的 Autofixture 在类被密封时不再提示缺少无参数构造函数?

转载 作者:行者123 更新时间:2023-12-04 06:39:43 27 4
gpt4 key购买 nike

当我使用 起订量 直接模拟 IBuilderFactory并实例化 BuilderService我自己在单元测试中,我可以通过测试来验证 Create() IBuilderFactory的方法只被调用一次。

但是,当我使用 时自动夹具 AutoMoq定制 ,卡住模拟 IBuilderFactory和实例化 BuilderServicefixture.Create<BuilderService> ,我得到以下异常:

System.ArgumentException: Can not instantiate proxy of class: OddBehaviorTests.CubeBuilder. Could not find a parameterless constructor. Parameter name: constructorArguments



如果我做 CubeBuilder密封(通过将其替换为由 SealedCubeBuilder 调用的密封类 IBuilderFactoryForSealedBuilder.Create() 来表示,使用 AutoFixture 和 AutoMoqCustomization 测试通过,没有抛出异常。

我错过了什么吗?由于我直接使用 Moq 获得通过测试,我相信这与 Autofixture 和/或 AutoMoqCustomization 有关。这是理想的行为吗?如果是这样,为什么?

为了重现,我正在使用:

using Moq;
using Ploeh.AutoFixture;
using Ploeh.AutoFixture.AutoMoq;
using Xunit;

以下是说明行为的四个测试:

public class BuilderServiceTests {
[Fact]
public void CubeBuilderFactoryCreateMethodShouldBeCalled_UsingMoq() {
var factory = new Mock<IBuilderFactory>();
var sut = new BuilderService(factory.Object);
sut.Create();
factory.Verify(f => f.Create(), Times.Once());
}
[Fact]
public void CubeBuilderFactoryCreateMethodShouldBeCalled_UsingAutoFixture() {
var fixture = new Fixture().Customize(new AutoMoqCustomization());
var factory = fixture.Freeze<Mock<IBuilderFactory>>();
var sut = fixture.Create<BuilderService>();
sut.Create(); // EXCEPTION THROWN!!
factory.Verify(f => f.Create(), Times.Once());
}
[Fact]
public void SealedCubeBuilderFactoryCreateMethodShouldBeCalled_UsingMoq() {
var factory = new Mock<IBuilderFactoryForSealedBuilder>();
var sut = new BuilderServiceForSealedBuilder(factory.Object);
sut.Create();
factory.Verify(f => f.Create(), Times.Once());
}
[Fact]
public void SealedCubeBuilderFactoryCreateMethodShouldBeCalled_UsingAutoFixture() {
var fixture = new Fixture().Customize(new AutoMoqCustomization());
var factory = fixture.Freeze<Mock<IBuilderFactoryForSealedBuilder>>();
var sut = fixture.Create<BuilderServiceForSealedBuilder>();
sut.Create();
factory.Verify(f => f.Create(), Times.Once());
}
}

以下是所需的类(class):

public interface IBuilderService { IBuilder Create(); }
public class BuilderService : IBuilderService {
private readonly IBuilderFactory _factory;
public BuilderService(IBuilderFactory factory) { _factory = factory; }
public IBuilder Create() { return _factory.Create(); }
}
public class BuilderServiceForSealedBuilder : IBuilderService {
private readonly IBuilderFactoryForSealedBuilder _factory;
public BuilderServiceForSealedBuilder(IBuilderFactoryForSealedBuilder factory) { _factory = factory; }
public IBuilder Create() { return _factory.Create(); }
}

public interface IBuilderFactoryForSealedBuilder { SealedCubeBuilder Create(); }
public interface IBuilderFactory { CubeBuilder Create(); }
public interface IBuilder { void Build(); }

public abstract class Builder : IBuilder {
public void Build() { } // build stuff
}

public class CubeBuilder : Builder {
private Cube _cube;
public CubeBuilder(Cube cube) { _cube = cube; }
}

public sealed class SealedCubeBuilder : Builder {
private Cube _cube;
public SealedCubeBuilder(Cube cube) { _cube = cube; }
}

public class Cube { }

最佳答案

如果您查看堆栈跟踪,您会注意到异常发生在 Moq 的深处。 AutoFixture 是一个固执己见的库,它持有的观点之一是 null 是无效的返回值 .出于这个原因,AutoMoqCustomization 像这样配置所有 Mock 实例:

mock.DefaultValue = DefaultValue.Mock;

(除其他事项外)。因此,您可以在没有 AutoFixture 的情况下完全重现失败的测试:
[Fact]
public void ReproWithoutAutoFixture()
{
var factory = new Mock<IBuilderFactory>();
factory.DefaultValue = DefaultValue.Mock;
var sut = new BuilderService(factory.Object);
sut.Create(); // EXCEPTION THROWN!!
factory.Verify(f => f.Create(), Times.Once());
}

奇怪的是,它似乎仍然适用于密封类。然而,这并不完全正确,而是源于 OP 测试 False Negatives .

考虑这个 Characterization Test起订量:
[Fact]
public void MoqCharacterizationForUnsealedClass()
{
var factory = new Mock<IBuilderFactory>();
factory.DefaultValue = DefaultValue.Mock;
Assert.Throws<ArgumentException>(() => factory.Object.Create());
}

Moq 正确抛出异常,因为它被要求创建 CubeBuilder 的实例,但它不知道如何做到这一点,因为 CubeBuilder 没有默认构造函数,也没有 Setup告诉它如何处理对 Create 的调用.

(顺便说一句,这里具有讽刺意味的是,AutoFixture 完全能够创建 CubeBuilder 的实例,但 Moq 中没有可扩展点使 AutoFixture 能够进入并接管 Moq 的默认对象实例创建行为。)

当返回类型被密封时,现在考虑这个特性测试:
[Fact]
public void MoqCharacterizationForSealedClass()
{
var factory = new Mock<IBuilderFactoryForSealedBuilder>();
factory.DefaultValue = DefaultValue.Mock;
var actual = factory.Object.Create();
Assert.Null(actual);
}

事实证明,在这种情况下,尽管已被隐式告知不要返回 null ,无论如何,Moq 都会这样做。

我的理论是,真正发生的事情是在上面的 MoqCharacterizationForUnsealedClass 中,什么 factory.DefaultValue = DefaultValue.Mock;真正的意思是 Moq 创建了一个 CubeBuilder 的模拟——换句话说,它动态地发出一个派生自 CubeBuilder 的类。但是,当要求创建 SealedCubeBuilder 的模拟时,它不能,因为它不能创建从密封类派生的类。

它没有抛出异常,而是返回 null .这是不一致的行为,并且 I've reported this as a bug in Moq .

关于dependency-injection - 为什么带有 AutoMoqCustomization 的 Autofixture 在类被密封时不再提示缺少无参数构造函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18155015/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com