依赖注入 – 为什么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. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |