2017-04-04 40 views
1

我单元测试类,需要一个工厂,因为它是一个依赖,并使用此构建的SUT有效控制对象:与名创建对象,以注入单元测试

class SystemUnderTest 
{ 
    private readonly IFoo foo1; 

    private readonly IFoo foo2; 

    public SystemUnderTest(IFooFactory fooFactory) 
    { 
     this.foo1 = fooFactory.Build("Bar1"); 
     this.foo2 = fooFactory.Build("Bar2"); 
    } 

    public IFoo Foo1 
    { 
     get 
     { 
      return this.foo1; 
     } 
    } 

    ... 
} 

SystemUnderTest对象具有被注入多个ViewModels一个单一生使用它的属性/方法来执行不同的操作,所以这有效地排除了定义Provider类,这样我可以注入foo1foo2没有与构造内的工厂接口这是我在面对这个问题时通常会这样做。

但是通过注入工厂,我无法与AutoFixture找出我怎么能正确地配置fooFactory提供正确的IFoo,并通过这种方法参数为我的测试,如:

[Theory] 
[AutoData] 
internal void Foo1_IsCorrectlyPopulated_Test(
    [Frozen] IFoo foo1, 
    SystemUnderTest systemUnderTest) 
{ 
    var actual = systemUnderTest.Foo1; 

    Assert.Same(foo1, actual); 
} 

我明白,我可以扩展AutoData属性,并提供我自己的定制灯具如:

class SystemUnderTestAutoDataAttribute : AutoDataAttribute 
{ 
    public SystemUnderTestAutoData() 
    { 
     var fooFactory = this.Fixture.Freeze<IFooFactory>(); 
     var foo1 = this.Fixture.Create<IFoo>(); 

     Mock.Get(fooFactory).Setup(m => m.Build("Bar1")).Returns(foo1); 
    } 
} 

但我不知道是否有获得第能力e foo1我在属性构造函数中创建的对象,并将它作为参数传递给测试方法?我明白,我可以Freezefoo1对象,但是这意味着我需要有两个属性类foo1foo2提供正确的信息,我的测试方法,并且此下降时,我有我的测试案例中使用这两个对象了。

我希望有,我可以创建一个特定的名称(或其他一些匹配方法)的对象和匹配的测试案例中一样(不编译)道:

[Theory] 
[SystemUnderTestAutoData] 
internal void Foo1_IsCorrectlyPopulated_Test(
    [Frozen(Matching.CreationName)] IFoo foo1, 
    SystemUnderTest systemUnderTest) 
{ 
    ... 
} 

class SystemUnderTestAutoDataAttribute : AutoDataAttribute 
{ 
    public SystemUnderTestAutoData() 
    { 
     var fooFactory = this.Fixture.Freeze<IFooFactory>(); 
     var foo1 = this.Fixture.Create<IFoo>(creationName: @"foo1"); 

     Mock.Get(fooFactory).Setup(m => m.Build("Bar1")).Returns(foo1); 
    } 
} 

因此,任何具有名称(或其他匹配方法)的匹配注册实例都可以作为测试用例参数的一部分来解析?

回答

3

类似下面应该工作。首先,定义一个[AutoMoqData]属性是这样的:

public class AutoMoqDataAttribute : AutoDataAttribute 
{ 
    public AutoMoqDataAttribute() 
     : base(new Fixture().Customize(new AutoMoqCustomization())) 
    { 
    } 
} 

二,编写测试是这样的:

[Theory, AutoMoqData] 
public void MyTest([Frozen]Mock<IFooFactory> td, IFoo foo1, IFoo foo2, IFixture fixture) 
{ 
    td.Setup(f => f.Build("Bar1")).Returns(foo1); 
    td.Setup(f => f.Build("Bar2")).Returns(foo2); 
    var sut = fixture.Create<SystemUnderTest>(); 

    // Rest of test... 
} 

也就是说,使用工厂处理一生的问题是几乎总是一个设计的气味。 OP中的特定设计违反了Nikola Malovic's 4th law of IoC。考虑将对象的设计与其生命周期管理分离。一种方法可以使用Decoraptor

+0

感谢马克我认为可能是最简单的方式,但我想看看是否有东西,这将使不具备手动创建测试的'SUT',但是这是好的。在设计方面,我明白,这是一种气味,但我很难看到一个'Decoraptor'将如何协助在这种情况下,在我的两个'客户端(视图模型)'和'Service'有效有辛格尔顿的寿命,因为服务在我的情况下,商店状态,每当这个状态被访问将需要每次重建服务? –

+1

@StephenRoss这是一个新问题;) –