我正在使用AutoFixture,Moq和XUnit扩展([Theory]属性),如本博文
http://blog.ploeh.dk/2010/10/08/AutoDataTheorieswithAutoFixture中所述.
我注意到大多数单元测试看起来像这样:
[Theory,AutoMoqData] public void Test( [Frozen] Mock<IServiceOne> serviceOne,[Frozen] Mock<IServiceTwo> serviceTwo,MyClass classUnderTest) { // Arrange serviceOne .Setup(m => m.Get(It.IsAny<int>())); serviceTwo .Setup(m => m.Delete(It.IsAny<int>())); // MyClass has a constructor with arguments for IServiceOne,and IServiceTwo // classUnderTest will use the two mocks specified above // Act var result = classUnderTest.Foo(); // Assert Assert.True(result); }
与总是使用[Frozen]装饰模具相反,是否有办法设置夹具以始终冻结模具?
这是AutoMoqData属性:
public class AutoMoqDataAttribute : AutoDataAttribute { public AutoMoqDataAttribute() : base(new Fixture().Customize(new AutoMoqCustomization())) { } }
解决方法
虽然它目前不是内置的,但是编写一个通用的Decorator很容易,当它们离开AutoFixture Tree of Responsibility时冻结对象:
public class MemoizingBuilder : ISpecimenBuilder { private readonly ISpecimenBuilder builder; private readonly ConcurrentDictionary<object,object> instances; public MemoizingBuilder(ISpecimenBuilder builder) { this.builder = builder; this.instances = new ConcurrentDictionary<object,object>(); } public object Create(object request,ISpecimenContext context) { return this.instances.GetOrAdd( request,r => this.builder.Create(r,context)); } }
请注意,它装饰另一个ISpecimenBuilder,但在返回之前记住所有值.如果同一请求再次到达,它将返回memoized值.
虽然您无法扩展AutoMoqCustomization,但您可以复制它的功能(它只有两行代码),并使用它周围的MemoizingBuilder:
public class AutoFreezeMoq : ICustomization { public void Customize(IFixture fixture) { if (fixture == null) throw new ArgumentNullException("fixture"); fixture.Customizations.Add( new MemoizingBuilder( new MockPostprocessor( new MethodInvoker( new MockConstructorQuery())))); fixture.ResidueCollectors.Add(new MockRelay()); } }
使用此AutoFreezeMoq而不是AutoMoqCustomization.它将冻结所有模拟,以及从这些模拟创建的所有接口和抽象基类.
public class AutoMoqDataAttribute : AutoDataAttribute { public AutoMoqDataAttribute() : base(new Fixture().Customize(new AutoFreezeMoq())) { } }