Юнит тесты против интеграционных тестов с Spring - PullRequest
38 голосов
/ 11 ноября 2008

Я работаю над проектом Spring MVC, и у меня есть модульные тесты для всех различных компонентов в дереве исходного кода.

Например, если у меня есть контроллер HomeController, в который нужно вставить LoginService, то в моем модульном тесте HomeControllerTest я просто создаю экземпляр объекта как обычный (вне Spring) и внедряю свойство:

protected void setUp() throws Exception {
    super.setUp();
    //...
    controller = new HomeController();
    controller.setLoginService( new SimpleLoginService() );
    //...
}

Это прекрасно работает для тестирования каждого компонента как изолированного модуля - за исключением того, что у меня есть несколько десятков классов в проекте, после написания класса и написания для него успешного модульного теста я постоянно забываю обновить свой контекст Spring MVC файл, который выполняет фактическое подключение к развернутому приложению. Я обнаружил, что я забыл обновить файл контекста при развертывании проекта в Tomcat и найти группу NullPointers из не подключенных компонентов.

Итак, вот мои вопросы:

  1. Это мой первый проект Spring - нормально ли создавать модульные тесты для отдельных bean-компонентов, как я это сделал, а затем создавать второй набор тестов (интеграционных тестов), чтобы проверить, что все работает так, как ожидается фактический контекст приложения? Есть ли признанная лучшая практика для этого?

  2. Кроме того, как вы отделите модульные тесты от интеграционных тестов? У меня есть весь исходный код в src, модульные тесты в test - должна ли быть вторая папка тестов (например, test-integration) для интеграционных тестов?

Так как это мой первый весенний проект, мне любопытно, как другие обычно делают подобные вещи, и вместо того, чтобы заново изобретать колесо, я скорее спрашиваю остальное сообщество.

Ответы [ 7 ]

32 голосов
/ 11 ноября 2008

Я не могу говорить о том, чтобы быть лучшей практикой, но вот что я делал в прошлом.

Юнит-тесты:

  • Создание модульных тестов для нетривиальных bean-компонентов (т. Е. Большинства ваших bean-компонентов, связанных с Spring)
  • Используйте Mocks для внедряемых служб, где это возможно (т. Е. Большинство, если не всегда).
  • Используйте стандартное соглашение об именах для этих тестов в каталоге проекта test. Использование Test или TestCase в качестве префикса или суффикса имени класса, по-видимому, широко практикуется.

Интеграционные тесты:

  • Создайте AbstractIntegrationTestCase, который устанавливает Spring WebApplicationContext для использования в выражениях интегрального теста.
  • Используйте соглашение об именах для интеграционных тестов в каталоге test. Я использовал IntTest или IntegrationTest в качестве префикса или суффикса для этих тестов.

Установите три цели Муравья test:

  1. test-all (или как вы хотите его назвать): запуск модульных и интеграционных тестов
  2. test: Выполнить юнит-тесты (просто потому, что test, кажется, наиболее распространенное использование для юнит-тестирования
  3. тест-интеграция: запустить интеграционные тесты.

Как уже отмечалось, вы можете использовать соглашения об именах, которые имеют смысл для вашего проекта.

Что касается отделения модуля от интеграционных тестов в отдельном каталоге, я не думаю, что это имеет значение, пока разработчики и их инструменты могут легко находить и выполнять их.

В качестве примера, последний Java-проект, над которым я работал с Spring, использовал именно то, что описано выше, с интеграционными и модульными тестами, расположенными в одном каталоге test. С другой стороны, проекты Grails явно разделяют каталоги модульных и интеграционных тестов в общем каталоге тестов.

6 голосов
/ 15 августа 2010

Несколько изолированных точек:

Да, это обычный подход к тестированию Spring - отдельные модульные тесты и интеграционные тесты, где первый не загружает какой-либо контекст Spring.

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

Если ваши тесты связаны между собой множеством зависимостей, то это не совсем юнит-тесты. Это интеграционные тесты, в которых вы проводите зависимости, используя новые, а не внедрение зависимостей. Потеря времени и дублирующих усилий, когда ваше производственное приложение использует Spring!

Полезны базовые интеграционные тесты для ознакомления с контекстами Spring.

Аннотация @required может помочь вам убедиться, что вы перехватываете необходимые зависимости в вашей проводке Spring.

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

4 голосов
/ 11 ноября 2008

Большая часть утомительного двойного бухгалтерского учета с пружиной исчезнет, ​​если вы также переключитесь на чисто аннотированный режим, где вы аннотируете все свои компоненты с помощью @Component, @Controller, @Service и @Repository. Просто добавьте @Autowired к атрибутам, которые нужно ввести.

См. Раздел 3.11 справочного руководства по пружине. http://static.springframework.org/spring/docs/2.5.x/reference/beans.html#beans-annotation-config

В соответствующей заметке мы использовали тесты Unit / Integratrion, описанные KenG. В моем последнем режиме мы также ввели третий «класс» тестов, «ComponentTests». Они работают с полной пружинной разводкой, но с реализациями заглушки с проводкой (с использованием фильтров компонентного сканирования и аннотаций весной).

Причина, по которой мы это сделали, заключалась в том, что для некоторого «сервисного» уровня у вас получилось ужасное количество логики проводки с ручным кодированием, чтобы вручную подключать бин, а иногда и нелепое количество фиктивных объектов. 100 линий разводки на 5 тестовых линий не редкость. Тесты компонентов устраняют эту проблему.

2 голосов
/ 11 ноября 2008

Используйте интерфейс InitializingBean (реализует метод afterPropertiesSet) или укажите init-метод для ваших bean-компонентов. InitializingBean обычно проще, потому что вам не нужно не забывать добавлять метод init к вашим bean-компонентам.

Используйте afterPropertiesSet, чтобы убедиться, что все введено как ненулевое значение, если оно равно NULL, выведите исключение.

0 голосов
/ 17 марта 2013

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

0 голосов
/ 16 сентября 2009

Что касается запуска модульных тестов отдельно от интеграционных тестов, я помещаю все последние в каталог интеграционных тестов и запускаю их, используя IDE / Ant, используя такой подход, как this . У меня работает.

0 голосов
/ 11 ноября 2008

Когда я создавал интеграционные тесты для веб-приложений, я помещал их в отдельный каталог. Они построены с использованием jUnit или TestNG и взаимодействуют с тестируемой системой, используя что-то вроде Selenium , которое попадает на веб-страницы, как если бы они были пользователями. Цикл будет выглядеть следующим образом: компилировать, запускать модульные тесты, создавать веб-приложение, развертывать его на работающем сервере, выполнять тесты, отменять развертывание приложения и сообщать о результатах. Идея состоит в том, чтобы проверить всю систему.

...