2013-09-23 16 views
2

我是新来AutoFixture,我试图创建在球队少TDD倾斜的开发者我的测试情况下友好的扩展送入MVC控制器通过AutoFixture。下面是代码:管理服务的通用测试帮手

public class HomeController : Controller 
{ 
    private readonly ISomeService _someService; 

    public HomeController(ISomeService someService) 
    { 
     _someService = someService; 
    } 

    public ActionResult Index() 
    { 
     _someService.SomeMethod(); 
     return View("Index"); 
    } 
} 

public class ControllerContext<T> where T : Controller 
{ 
    protected static T ControllerUnderTest; 
    private static IFixture _fixture; 

    public ControllerContext() 
    { 
     _fixture = new Fixture().Customize(new AutoMoqCustomization()); 
     _fixture.Customize<ControllerContext>(c => c.Without(x => x.DisplayMode)); 
     ControllerUnderTest = _fixture.Create<T>(); 
    } 

    protected static Mock<TDouble> For<TDouble>() where TDouble : class 
    { 
     //var mock = _fixture.Create<TDouble>(); 
     var mock = _fixture.Create<Mock<TDouble>>(); 
     return mock; 
    } 
} 

所以扩展是For方法 - 当我检查ControllerUnderTest具有注入“ISomeService”它注入了就好了一个实例,它肯定叫我主张对方法。当我检查的“对”的方法似乎是同一版本作为一个注入控制器创建的模拟,但它不会Verif Y!

public class EXAMPLE_When_going_to_home_page : ControllerContext<HomeController> 
{ 
    Because of =() => 
    { 
     ControllerUnderTest.Index(); 

    }; 

    It should_do_something =() => 
    { 
     //This throws a 'Invocation was not performed' 
     For<ISomeService>().Verify(x => x.SomeMethod()); 
    }; 

    Establish context =() => 
    { 

    }; 
} 

我努力寻找做类似的东西别人的任何例子,我知道我肯定做一些愚蠢的事在这里,但在我的脑海这个测试应该通过?

回答

5

Create每次都会创建一个新的匿名实例,除非您冻结(通过.Freeze<T>()或AutoFixture.Xunit的[Frozen])一个实例。这意味着注入HomeController的值与For返回的值不同。

有几种可能的解决方案,所有这一切最终将涉及冻结值或注入之一来使用。

一个例子是这样的:

public class ControllerContext<T> where T : Controller 
{ 
    private static Lazy<T> _controllerFactory; 
    private static IFixture _fixture; 

    public ControllerContext() 
    { 
     _fixture = new Fixture().Customize(new AutoMoqCustomization()); 
     _fixture.Customize<ControllerContext>(c => c.Without(x => x.DisplayMode)); 
     _controllerFactory = new Lazy<T>(() => _fixture.Create<T>()); 
    } 

    protected static Mock<TDouble> For<TDouble>() where TDouble : class 
    { 
     var mock = _fixture.Freeze<Mock<TDouble>>(); 
     return mock; 
    } 

    protected static T ControllerUnderTest 
    { 
     get { return _controllerFactory.Value; } 
    } 
} 

public class EXAMPLE_When_going_to_home_page : ControllerContext<HomeController> 
{ 
    static Mock<ISomeService> SomeService; 

    Because of =() => 
    { 
     SomeService = For<ISomeService>(); 
     ControllerUnderTest.Index(); 
    }; 

    It should_do_something =() => 
    { 
     //This throws a 'Invocation was not performed' 
     SomeService.Verify(x => x.SomeMethod()); 
    }; 

    Establish context =() => 
    { 

    }; 
} 

的这个变化版本最重要的一点是,第一Freeze被称为在服务模拟,只有创建的控制器的匿名实例之后。由于现在使用For方法,您应该将其重命名为GetService

+0

这完全不是那么回事。应在“静态ISomeService SomeService”是“静态模拟 SomeService”,还是应该the'For”方法的变化是一个真正的实例 – Grace

+0

@Grace:它应该是'模拟'固定 –

+0

如果什么。测试客户端调用'For'方法之前读取'ControllerUnderTest'财产 –

1

你会最终落得在痛苦的世界,如果你走有static状态管理服务和SUT之间的相互作用的方式的道路。其中一个原因是,例如单元测试应该是并行(如xUnit.net V2但最终所有的测试框架,因为它很有道理)

您可以add Customizations to AutoFixture to allow natural creation of MVC Controllers根据需要,然后它只是一个在喂食或冻结定制依赖文件的问题有必要的。

我强烈建议花时间改变你的测试结构,让AutoFixture以声明方式创建控制器 - 看看AutoFixture.Xunit有什么可能,并用它来告诉你如何构造你正在使用的测试助手在您的规格。

(一些背景 - 我一直在使用SubSpec这一切规格东西房前屋后,并最终结束了AutoFixture.Xunit更快乐 - 它只是更简单,更可组合

+1

我也从带有手动AutoFixture的MSpec切换到自动AutoFixtre集成的xUnit。 +1 –