2013-09-23 2 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» он имеет экземпляр впрыскивается просто отлично, и это, безусловно, вызывает метод я утверждающая против. Когда я проверяю макет, созданный в методе «For», он выглядит такой же, как тот, который вводится в контроллер, но он не будет 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's [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» быть «статическим Mock SomeService», или если метод «For» изменился как реальный экземпляр? – Grace

+0

@Grace: он должен быть 'Mock '. Исправлено. –

+0

Что делать, если тестовый клиент читает свойство 'ControllerUnderTest' перед вызовом метода' For'? –

1

В конечном итоге вы в конечном итоге окажетесь в мире боли, если вы пойдете по пути определения состояния static как способа управления взаимодействием между службами и SUT. Одной из причин является, например, то, что модульные тесты должны быть параллельны (например, xUnit.net v2, но, в конечном счете, все тестовые среды, поскольку это имеет смысл)

При необходимости можно использовать add Customizations to AutoFixture to allow natural creation of MVC Controllers, а затем это просто вопрос подачи или зависания настраиваемых зависимостей как надо.

Я бы предпочел потратить время на изменение структуры ваших тестов, чтобы AutoFixture создавал Контроллер декларативно - взгляните на то, что возможно на AutoFixture.Xunit, и используйте это, чтобы сообщить, как вы структурируете тестовые помощники, которые используете в ваших спецификациях.

(Некоторые - я был вокруг дома со всем этим Spec материала с использованием SubSpec и в конечном итоге в конечном итоге гораздо счастливее с AutoFixture.Xunit - это просто проще и компонуемы

+1

Я также переключился с MSpec с ручной AutoFixture на xUnit с автоматической интеграцией AutoFixtre. +1 –