Автоматизированные Тесты. Продолжение
Начало
Продолжение
Фреймворки-имитаторы
Фреймворки-имитаторы могут быть полезны для тестирования сценариев, которые трудно воспроизвести. Например, проверить, может ли приложение обрабатывать определённую ошибку, возвращаемую сторонним API. Тогда можно имитировать API, чтобы вернуть ожидаемую ошибку.
Я стараюсь избегать использования фреймворков-имитаторов, а создавать тестовые двойники вручную. Тестовые двойники могут более точно имитировать реальные сценарии, чем фреймворки-имитаторы. Они могут сохранять состояние между несколькими вызовами методов, что может быть сложно с фреймворками-имитаторами. Может потребоваться убедиться, что мок вызывается в правильном порядке (например, Save перед Get – хотя это можно сделать), или что вызывается правильное количество раз.
Кроме того, при отладке тестов можно легко перейти в код тестового двойника, тогда как это не всегда осуществимо с фреймворками-имитаторами. Добавляя пользовательские методы к тестовым двойникам для настройки или проверки содержимого, вы можете повысить выразительность, сделав тесты более читаемыми и простыми в обслуживании. Тестовые двойники также можно повторно использовать в нескольких тестах.
Это может показаться более трудоёмким, но эти усилия не существенны. Обычно вы не имеете дело с чрезмерным количеством фиктивных классов, и можете повторно использовать обобщённую реализацию в различных тестах. Так вы избегаете дублирования блоков кода для настройки в каждом тесте.
// Используем фреймворк-имитатор FakeItEasy
// Что, если сервис вызовет GetAsync до SaveAsync?
var service = A.Fake<IService>();
A.CallTo(() => service.SaveAsync()).Returns(true);
A.CallTo(() => service.GetAsync()).Returns(["value"]);
var sut = new MyClass(service);
sut.DoSomething();
Сравните с:
// Используем собственный двойник
var service = new StubService();
service.AddItem("value"); // Добавляем данные
var sut = new MyClass(service);
sut.DoSomething();
Нужно ли добавлять интерфейсы, чтобы иметь возможность имитировать?
Интерфейсы с единственной реализацией бесполезны. Они просто добавляют сложности коду. По возможности не усложняйте код только для того, чтобы иметь возможность использовать моки. Если в коде будет единственная реализация, то вам может не понадобиться интерфейс. Кроме того, можно создать мок самого класса, а не интерфейса.
- Используйте виртуальные методы, чтобы можно было переопределить поведение в производном классе в тесте.
- Используйте делегаты (действие для переопределения поведения), чтобы можно было предоставить пользовательскую реализацию в тесте.
- Предоставьте конфигурацию, чтобы пользователь мог предоставить пользовательскую реализацию.
Окончание следует…
Источник: https://www.meziantou.net/automated-tests.htm
Автор оригинала: Gérald Barré