但是,当我使用Autofixture与AutoMoqCustomization,冻结IBuilderFactory的模拟并使用fixture.Create< BuilderService>实例化BuilderService时,我得到以下异常:
System.ArgumentException: Can not instantiate proxy of class: OddBehaviorTests.CubeBuilder. Could not find a parameterless
constructor. Parameter name: constructorArguments
如果我将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 { }
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.