Использование IoC для модульного тестирования

97

Как можно использовать контейнер IoC для модульного тестирования? Полезно ли управлять макетами в огромном решении (более 50 проектов) с помощью IoC? Есть опыт? Любые библиотеки C #, которые хорошо подходят для использования в модульных тестах?

краушер
источник
7
@Mark Seemann был бы слишком скромным, чтобы указать на это, но если вас интересует этот вопрос, вы должны хотя бы знать об AutoFixture
Рубен Бартелинк,
1
Там хороший разговор об отношениях между DI и насмешливым на Vimeo Мигель Кастро: vimeo.com/68390510
GregC

Ответы:

131

Вообще говоря, контейнер DI не должен быть необходим для модульного тестирования, потому что модульное тестирование - это разделение ответственности.

Рассмотрим класс, который использует внедрение конструктора

public MyClass(IMyDependency dep) { }

Во всем вашем приложении может быть скрытый огромный граф зависимостей IMyDependency, но в модульном тесте вы сводите все это до одного Test Double .

Вы можете использовать динамические макеты, такие как Moq или RhinoMocks, для создания Test Double, но это не обязательно.

var dep = new Mock<IMyDependency>().Object;
var sut = new MyClass(dep);

В некоторых случаях может быть неплохо иметь контейнер с автоматическим макетом , но вам не нужно использовать тот же контейнер DI, который использует производственное приложение.

Марк Симанн
источник
13
согласовано ... если у цели тестирования нет контейнера IoC в качестве зависимости, вашим тестам они не понадобятся ... вы собираетесь удалить большую часть графа объектов при выполнении своих модульных тестов.
Андерсон Имес,
4
@Mark Seemann В этом есть смысл ... А как насчет интеграционных тестов? Т.е. я поигрался с UI-тестами и столкнулся с ситуацией, когда мне нужно было делиться корнем композиции. Любые комментарии?
Арнис Лапса
5
@Arnis L .: Для интеграционных тестов это менее важно. Вы можете использовать контейнер DI для подключения компонентов, но если это так, вам, вероятно, потребуется другая конфигурация для контейнера, чем в полном приложении - если вы не выполните подкожный тест или полный тест системы, и в этом случае вы можете повторно использовать конфигурацию контейнера приложения.
Марк Земанн
Ссылка на журнал
msdn
18

Как можно использовать контейнер Ioc для модульного тестирования?

IoC будет применять парадигмы программирования, которые упростят изолированное модульное тестирование (то есть с использованием моков): использование интерфейсов, без new (), без синглтонов ...

Но использование контейнера IoC для тестирования на самом деле не является требованием, он просто предоставит некоторые возможности, например, внедрение имитаций, но вы можете сделать это вручную.

Полезно ли управлять mock-объектами в огромном решении (50+ проектов) с помощью IoC?

Я не совсем понимаю, что вы имеете в виду, говоря об управлении макетами с помощью IoC. Во всяком случае, контейнеры IoC обычно могут делать больше, чем просто вводить макеты, когда дело доходит до тестирования. И если у вас есть достойная поддержка IDE, которая делает возможным рефакторинг, почему бы не использовать ее?

Есть опыт?

Да, для огромного решения вам больше, чем когда-либо, нужно решение, не подверженное ошибкам и нежелательное для рефакторинга (т.е. либо через типобезопасный контейнер IoC, либо через хорошую поддержку IDE).

Паскаль Тивент
источник
17

Я часто использую в своих тестах контейнер IoC. Конечно, они не являются «юнит-тестами» в чистом смысле. IMO Они больше BDDish и облегчают рефакторинг. Тесты призваны дать вам уверенность в необходимости рефакторинга. Плохо написанные тесты могут быть похожи на заливку цемента в ваш код.

Учтите следующее:

[TestFixture]
public class ImageGalleryFixture : ContainerWiredFixture
{
    [Test]
    public void Should_save_image()
    {
        container.ConfigureMockFor<IFileRepository>()
            .Setup(r => r.Create(It.IsAny<IFile>()))
            .Verifiable();

        AddToGallery(new RequestWithRealFile());

        container.VerifyMockFor<IFileRepository>();
    }

    private void AddToGallery(AddBusinessImage request)
    {
        container.Resolve<BusinessPublisher>().Consume(request);
    }
}

При добавлении изображения в галерею происходит несколько вещей. Размер изображения изменяется, создается эскиз, а файлы сохраняются на AmazonS3. Используя контейнер, мне легче изолировать только то поведение, которое я хочу протестировать, которое в данном случае является постоянной частью.

При использовании этой техники может пригодиться автоматическое расширение контейнера: http://www.agileatwork.com/auto-mocking-unity-container-extension/

Майк Валенти
источник
8
+1 за фразу «как заливать цемент в код». Я начал постоянно им пользоваться.
Эндрю Шеперд
2

Используя контейнеры с возможностью разрешать незарегистрированные / неизвестные службы, такие как SimpleInjector , DryIoc (его моя шахта) может возвращать фиктивные сообщения для еще не реализованных интерфейсов.

Это означает, что вы можете начать разработку с первой простой реализации и ее фиктивных зависимостей и по мере продвижения заменять их реальными вещами.

дадхи
источник