依赖注入 – 为什么Autofixture w / AutoMoqCustomization在类密封时停止抱怨缺乏无参数
|
当我直接使用Moq来模拟IBuilderFactory并在单元测试中自己实例化BuilderService时,我可以得到一个通过测试,它验证IBuilderFactory的Create()方法只被调用一次. 但是,当我使用Autofixture与AutoMoqCustomization,冻结IBuilderFactory的模拟并使用fixture.Create< BuilderService>实例化BuilderService时,我得到以下异常:
如果我将CubeBuilder密封(用IBuilderFactoryForSealedBuilder.Create()调用的密封类SealedCubeBuilder代替它,测试将使用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());
}
}
以下是必需的类: 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是一个固定意见的库,其中的一个观点是空值是无效的返回值.因此,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. 考虑一下Moq的Characterization Test: [Fact]
public void MoqCharacterizationForUnsealedClass()
{
var factory = new Mock<IBuilderFactory>();
factory.DefaultValue = DefaultValue.Mock;
Assert.Throws<ArgumentException>(() => factory.Object.Create());
}
Moq正确地引发异常,因为它被要求创建CubeBuilder的一个实例,并且它不知道如何做到这一点,因为CubeBuilder没有默认构造函数,并且没有安装程序告诉它如何处理对Create的调用. (另外,具有讽刺意味的是,AutoFixture完全能够创建CubeBuilder的实例,但Moq中没有可扩展性点,使AutoFixture可以进入并接管Moq的默认对象实例创建行为.) 现在,在密封返回类型时考虑此Characterization测试: [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. (编辑:安卓应用网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
