Всем привет. В предыдущей статье мы рассмотрели тестируемое приложение. Прежде чем сразу приступать к написанию кода пару слов расскажу о группах юнит-тестов. Все модульные тесты можно условно разделить на: тесты состояния (state based) и тесты взаимодействия (interaction tests).
Тесты состояния — это тесты, проверяющие, что вызываемый метод объекта отработал корректно, проверяя состояние тестируемого объекта после вызова этого метода.
Тесты взаимодействия — тесты, в которых тестируемый объект производит манипуляции с другими объектами. Такие тесты применяются тогда, когда требуется удостовериться, что тестируемый объект корректно взаимодействует с другими объектами.
Стоит также обратить внимание, что модульный тест может запросто превратиться в интеграционный тест, если при тестировании используется реальное окружение — база данных, файловая система и т. д.
Интеграционные тесты — тесты, проверяющие работоспособность двух или более модулей системы, но в совокупности — нескольких объектов как единого блока.
В тестах взаимодействия тестируется конкретный объект и то, как именно он взаимодействует с внешними зависимостями.
Внешняя зависимость — это объект, с которым взаимодействует код и над которым нет прямого контроля. Для ликвидации внешних зависимостей в модульных тестах используются тестовые объекты как, например, stubs (заглушки).
Когда-то прочитав труд Жерарда Месароша под названием “xUnit test patterns: refactoring test code“, где автор вводит 5 видов тестовых объектов, выделю основные из них:
1) test spy (тестовый шпион), используется для тестов взаимодействия. Основным функционалом является запись данных или вызовов, поступающих из тестируемого объекта для последующей проверки корректности вызова зависимого объекта. Позволяет проверить логику тестируемого объекта без проверок зависимых объектов.
2) test stub (заглушка), используется для получения данных из внешней зависимости, подменяя её. При этом игнорируются все данные, которые могут поступать из тестируемого объекта в stub. Это один из самых популярных видов тестовых объектов.
3) dummy object, — обычно передается в тестируемый класс в качестве параметра, но не имеет поведения. С ним ничего не происходит, никакие методы не вызываются. Примером таких dummy-объектов являются new object(), null и т.д.
4) fake object (фальшивый объект), используется в основном для запуска тестов (незапускаемых) и ускорения их работы. Основные примеры — эмулятор для конкретного приложения БД в памяти (fake database) или фальшивый веб-сервис.
5) mock object (мок-объект), очень похож на тестовый шпион, однако не записывает последовательность вызовов с переданными параметрами для последующей проверки, а может сам выкидывать исключения при некорректно переданных данных. Именно эти объект проверяет корректность поведения тестируемого объекта. Их мы и будем использовать на практике далее.
Для написания первого теста создадим юнит-тест проект и добавим в него ссылку на проект нашего приложения:
В нашем юнит-проекте нужно установить Moq:
Moq понадобится для создания “фейковой” версии интерфейса ICreditCheckerGateway.
Конечный тест проверки на возможность выдачи кредита заемщику будет выглядеть следующим образом:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
[Test] public void ShouldDeclineUnderAgeApplicant() { // use Moq to create a fake version of an ICreditCheckerGateway var fakeGateway = new Mock<ICreditCheckerGateway>(); // create the sut and pass the fake version to it's constructor var sut = new CreditCardApplicationScorer(fakeGateway.Object); // create some test data var application = new CreditCardApplication { ApplicantAgeInYears = 20 }; // execute the behaviour var result = sut.ScoreApplication(application); // check expected result Assert.That(result, Is.False); } |
Нашему заемщику меньше 21 года, значит наша проверка будет выглядеть так:
1 |
Assert.That(result, Is.False); |
Запустив тест, можно убедиться, что он выполняется успешно: