Модульные тесты и тесты интеграции с Spring
Я работаю над проектом Spring MVC, и у меня есть модульные тесты для всех различных компонентов в исходном дереве.
Например, если у меня есть контроллер HomeController
, который должен иметь в нем LoginService
, то в моем unit test HomeControllerTest
я просто создаю объект как обычный (вне Spring), и введите свойство:
protected void setUp() throws Exception {
super.setUp();
//...
controller = new HomeController();
controller.setLoginService( new SimpleLoginService() );
//...
}
Это отлично работает для тестирования каждого компонента как изолированного устройства - за исключением того, что у меня есть несколько десятков классов в проекте, после написания класса и написания успешного unit test для него я забываю обновить свой Spring Контекстный файл MVC, который выполняет фактическую проводку в развернутом приложении. Я узнал, что забыл обновить файл контекста, когда я развертываю проект в Tomcat и нахожу кучу NullPointers из непроводного beans.
Итак, вот мои вопросы:
-
Это мой первый проект Spring - нормально ли создавать отдельные тесты для отдельного beans, как я это сделал, а затем создать второй набор тестов (интеграционных тестов), чтобы проверить, что все работает как ожидалось с фактическим контекстом приложения? Существует ли для этого наилучшая практика?
-
Кроме того, как вы отделяете модульные тесты от тестов интеграции? У меня есть весь исходный код в src
, модульные тесты в test
- должна ли быть вторая тестовая папка (например, test-integration
) для тестовых случаев интеграции?
Так как это мой первый проект Spring, мне любопытно, как обычно обычно идут дела такие вещи - и вместо того, чтобы изобретать колесо, я скорее прошу остальных.
Ответы
Ответ 1
Я не могу говорить, что это лучшая практика, но вот то, что я делал в прошлом.
Единичные тесты:
- Создайте единичные тесты для нетривиального beans (т.е. большинство ваших Spring связанных beans)
- Используйте Mocks для инъецируемых сервисов, где это возможно (например, большинство, если не все время).
- Используйте стандартное соглашение об именах для этих тестов в каталоге проекта
test
. Использование test
или TestCase
в качестве префикса или суффикса для имени класса, по-видимому, широко практикуется.
Интеграционные тесты:
- Создайте
AbstractIntegrationTestCase
, который устанавливает Spring
WebApplicationContext
для использования в тестовых кластерах интеграции.
- Используйте соглашение об именах для тестов интеграции в каталоге
test
. Я использовал IntTest
или IntegrationTest
как префикс или суффикс для этих тестов.
Настройте три цели Ant test
:
- test-all (или что-то еще, что вы хотите назвать): Run Unit и Integration Tests
- test: выполнить тесты модуля (только потому, что
test
, по-видимому, является наиболее распространенным использованием для модульного тестирования
- test-integration: выполните тесты интеграции.
Как уже отмечалось, вы можете использовать соглашения об именах, которые имеют смысл для вашего проекта.
Что касается разделения блока от тестов интеграции на отдельный каталог, я не думаю, что это имеет значение, если разработчики и их инструменты могут легко находить и выполнять их.
В качестве примера, последний проект Java, над которым я работал с Spring, использовал именно то, что описано выше, с интеграционными тестами и модульными тестами, живущими в одном каталоге test
. С другой стороны, проекты Grails явно разделяют тестовые каталоги единичных и интеграционных тестов в общем тестовом каталоге.
Ответ 2
Несколько изолированных точек:
Да, это общий подход к тестированию Spring - отдельные модульные тесты и интеграционные тесты, где первый не загружает контекст Spring.
Для ваших модульных тестов, возможно, подумайте об издевательстве, чтобы убедиться, что ваши тесты сосредоточены на одном изолированном модуле.
Если вы тестируете проводку в тонне зависимостей, тогда они не являются модульными тестами. Это интеграционные тесты, в которых вы подключаете зависимости, используя новую, а не зависимую инъекцию. Пустая трата времени и дублирование усилий, когда ваше производственное приложение использует Spring!
Полезны базовые интеграционные тесты для создания контекстов Spring.
Аннотации @required могут помочь вам уловить необходимые зависимости в проводке Spring.
Возможно, загляните в Maven, который даст вам явные этапы, чтобы привязать тестирование вашего устройства и интеграции. Maven довольно широко используется в сообществе Spring.
Ответ 3
Много утомительной двойной книги с spring исчезает, если вы также переключаетесь на чисто аннотированный режим, где вы комментируете все свои beans с помощью @Component, @Controller, @Service и @Repository. Просто добавьте @Autowired к атрибутам, которые вам нужно ввести.
См. раздел 3.11 справочного руководства spring. http://static.springframework.org/spring/docs/2.5.x/reference/beans.html#beans-annotation-config
В соответствующей заметке мы используем тесты Unit/Integratrion, которые описывают KenG. В моем последнем режиме мы также представили третий "класс" тестов "ComponentTests". Они выполняются с полным проводником spring, но с проводными реализациями заглушек (с использованием фильтров компонентного сканирования и аннотаций в spring).
Причина, по которой мы это делали, заключалась в том, что для некоторого слоя "службы" вы получаете ужасное количество логики проводки с ручной кодировкой, чтобы вручную подключить bean, а иногда и смешное количество макет-объектов. 100 линий проводки для 5 линий испытания не являются редкостью. Тесты компонентов облегчают эту проблему.
Ответ 4
Используйте интерфейс InitializingBean (реализует метод afterPropertiesSet) или укажите метод init для вашего beans. InitializingBean обычно проще, потому что вам не нужно забывать добавлять метод init к вашему beans.
Использовать afterPropertiesSet для обеспечения того, чтобы все было введено как ненулевое, если оно равно null, введите исключение.
Ответ 5
Когда я создал интеграционные тесты для веб-приложений, я поместил их в отдельный каталог. Они создаются с использованием jUnit или TestNG и взаимодействуют с тестируемой системой, используя что-то вроде Selenium, которое попадает на веб-страницы, как если бы они были пользователями, Цикл будет выглядеть следующим образом: компиляция, запуск модульных тестов, создание веб-приложения, развертывание его на работающем сервере, выполнение тестов, удаление приложения и получение отчетов. Идея состоит в том, чтобы протестировать всю систему.
Ответ 6
Что касается запуска отдельных тестов отдельно от тестов интеграции, я помещаю их все в каталог интеграции и тестирования с помощью IDE/ Ant с помощью подхода, такого как this. Работает для меня.
Ответ 7
разница между unit test и тестом интеграции, unit test не обязательно загружает ваш контекст, вы сосредотачиваетесь на коде, который вы написали, - он работает быстро, то есть и без исключений, издеваясь над любыми зависит от вызовов в нем.
Но в случае интеграционных тестов вы загружаете контекст и выполняете сквозной тест, например, реальные сценарии.