Dependency injection 当类被密封时,为什么Autofixture w/AutoMoqCustomization停止抱怨缺少无参数构造函数?

Dependency injection 当类被密封时,为什么Autofixture w/AutoMoqCustomization停止抱怨缺少无参数构造函数?,dependency-injection,moq,castle-dynamicproxy,autofixture,automocking,Dependency Injection,Moq,Castle Dynamicproxy,Autofixture,Automocking,当我在单元测试中直接使用Moq模拟ibailderfactory并实例化BuilderService时,我可以得到一个通过的测试,该测试验证ibailderfactory的Create()方法只调用了一次 但是,当我将Autofixture与AutoMoqCustomization一起使用时,冻结了IBuilderFactory的模拟,并使用fixture.Create实例化了BuilderService,我得到了以下异常: System.ArgumentException:无法实例化类OddB

当我在单元测试中直接使用Moq模拟
ibailderfactory
并实例化
BuilderService
时,我可以得到一个通过的测试,该测试验证
ibailderfactory
Create()
方法只调用了一次

但是,当我将AutofixtureAutoMoqCustomization一起使用时,冻结了
IBuilderFactory的模拟,并使用
fixture.Create
实例化了
BuilderService
,我得到了以下异常:

System.ArgumentException:无法实例化类OddBehaviorTests.CubeBuilder的代理。找不到无参数的 构造器。参数名称:构造函数参数

如果我将
CubeBuilder
密封(用
ibilderfactoryforsealedbuilder.Create()
调用的密封类
SealedCubeBuilder
替换它),则使用AutoFixture和AutoMoqCustomization通过测试,不会引发异常

我遗漏了什么吗?因为我直接使用Moq通过测试,我相信这与自动夹具和/或自动Moq定制有关。这是期望的行为吗?如果是,为什么

为了复制,我使用:

使用Moq;
使用Ploeh.AutoFixture;
使用Ploeh.AutoFixture.AutoMoq;
使用Xunit;
以下是四项测试,说明了该行为:

公共类构建器服务测试{
[事实]
应使用MOQ()调用公共void CubeBuilderFactoryCreateMethod{
var factory=new Mock();
var sut=newbuilderservice(factory.Object);
sut.Create();
factory.Verify(f=>f.Create(),Times.Once());
}
[事实]
应使用AutoFixture()调用公共void CubeBuilderFactoryCreateMethod{
var fixture=new fixture().Customize(new AutoMoqCustomization());
var factory=fixture.Freeze();
var sut=fixture.Create();
sut.Create();//引发异常!!
factory.Verify(f=>f.Create(),Times.Once());
}
[事实]
应使用MOQ()调用公共无效密封的CubeBuilderFactoryCreateMethod{
var factory=new Mock();
var sut=newbuilderServiceForSealedBuilder(factory.Object);
sut.Create();
factory.Verify(f=>f.Create(),Times.Once());
}
[事实]
应使用AutoFixture()调用公共无效密封的CubeBuilderFactoryCreateMethod{
var fixture=new fixture().Customize(new AutoMoqCustomization());
var factory=fixture.Freeze();
var sut=fixture.Create();
sut.Create();
factory.Verify(f=>f.Create(),Times.Once());
}
}
以下是所需的课程:

公共接口IBuilderService{IBuilder Create();}
公共类构建器服务:IBuilderService{
私人只读IBuilderFactory(工厂);
公共构建器服务(IBuilderFactory){u factory=factory;}
公共IBuilder Create(){return\u factory.Create();}
}
公共类构建器ServiceForSealedBuilder:IBuilderService{
私有只读IBuilderFactoryForSealedBuilder\u工厂;
公共构建器ServiceForsealedBuilder(iBilderFactoryForsealedBuilder工厂){u factory=factory;}
公共IBuilder Create(){return\u factory.Create();}
}
sealedBuilder{SealedCubeBuilder Create();}的公共接口IBILderFactory
公共接口IBuilderFactory{CubeBuilder Create();}
公共接口IBuilder{void Build();}
公共抽象类生成器:IBuilder{
public void Build(){}//Build stuff
}
公共类CubeBuilder:Builder{
私有多维数据集;
公共CubeBuilder(多维数据集){u Cube=Cube;}
}
公共密封类密封立方体生成器:生成器{
私有多维数据集;
公共密封立方体生成器(立方体){{u Cube=Cube;}
}
公共类多维数据集{}

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

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());
}
事实证明,在这种情况下,尽管被隐式告知不要返回
null
,Moq还是这样做了

我的理论是,在上面的MoqCharacterizationforUnseledClass中,
factory.DefaultValue=DefaultValue.Mock;
真正的意思是Moq创建了一个CubeBuilder的模拟-换句话说,它动态地发出一个从CubeBuilder派生的类。然而,当被要求创建一个Sealed CubeBuilder的模拟时,它不能,因为它不能创建从密封类派生的类


它没有抛出异常,而是返回
null
。这是不一致的行为,而且。

感谢您破译了这一个!当答案被证明是我正在使用的库中的一个bug时,我的心情很复杂…我偶然发现了这一点,因为我的一些XXXBuilder类被密封了,一些没有。发现了关于是什么的争论在没有充分理由不密封的情况下密封类,或者做相反的事情。似乎密封类会导致单元测试摩擦-小心分享对这个问题的看法?密封类损害可测试性的概念起源于(我认为)来自Java,默认情况下所有成员都是虚拟的。在C#中,情况并非如此,因此问题变得不那么重要。在任何情况下,如果你喜欢组合而不是继承,这几乎不重要。FWIW、F#构造(如记录)编译为密封类,但F#代码仍然是非常可测试的。最终,如果你遵循TDD,你将拥有可测试代码:)我应该澄清,当使用中的模拟框架无法运行时,密封类似乎会损害可测试性
[Fact]
public void MoqCharacterizationForUnsealedClass()
{
    var factory = new Mock<IBuilderFactory>();
    factory.DefaultValue = DefaultValue.Mock;
    Assert.Throws<ArgumentException>(() => factory.Object.Create());
}
[Fact]
public void MoqCharacterizationForSealedClass()
{
    var factory = new Mock<IBuilderFactoryForSealedBuilder>();
    factory.DefaultValue = DefaultValue.Mock;
    var actual = factory.Object.Create();
    Assert.Null(actual);
}