Веб-интеграционные тесты позволяют проводить интеграционное тестирование приложения Spring Boot без каких-либо насмешек. Используя @WebIntegrationTest
и @SpringApplicationConfiguration
мы можем создавать тесты, которые загружают приложение и прослушивают обычные порты. Это небольшое дополнение к Spring Boot значительно упрощает создание интеграционных тестов с Selenium WebDriver.
Тест Зависимости
Приложение, которое мы будем тестировать, представляет собой простое приложение Spring Boot / Thymeleaf с зависимостями spring-boot-starter-web
, spring-boot-starter-thymeleaf
и spring-boot-starter-actuator
spring-boot-starter-thymeleaf
. Смотрите ссылки для ссылки на проект GitHub.
Тестовые зависимости:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
< dependency > < groupId >org.springframework.boot</ groupId > < artifactId >spring-boot-starter-test</ artifactId > < scope >test</ scope > </ dependency > < dependency > < groupId >org.assertj</ groupId > < artifactId >assertj-core</ artifactId > < version >1.5.0</ version > < scope >test</ scope > </ dependency > < dependency > < groupId >org.seleniumhq.selenium</ groupId > < artifactId >selenium-java</ artifactId > < version >2.45.0</ version > < scope >test</ scope > </ dependency > |
Тест веб-интеграции
С классическим Spring Test, используя MockMvc
, вы создали бы тест, как MockMvc
ниже:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
|
@RunWith (SpringJUnit4ClassRunner. class ) @SpringApplicationConfiguration (classes = Application. class ) @WebAppConfiguration public class HomeControllerClassicTest { @Autowired private WebApplicationContext wac; private MockMvc mockMvc; @Before public void setUp() throws Exception { mockMvc = MockMvcBuilders.webAppContextSetup(wac).build(); } @Test public void verifiesHomePageLoads() throws Exception { mockMvc.perform(MockMvcRequestBuilders.get( "/" )) .andExpect(MockMvcResultMatchers.status().isOk()); } } |
@SpringApplicationConfiguration
расширяет возможности @ContextConfiguration
и загружает контекст приложения для интеграционного теста. Чтобы создать тест без смоделированной среды, мы должны определить наш тест, используя аннотацию @WebIntegrationTest
:
1
2
3
4
5
6
|
@RunWith (SpringJUnit4ClassRunner. class ) @SpringApplicationConfiguration (classes = Application. class ) @WebIntegrationTest (value = "server.port=9000" ) public class HomeControllerTest { } |
Это запустит полное приложение в тесте JUnit, прослушивая порт 9000
. Имея такой тест, мы можем легко добавить Selenium и выполнить реальные функциональные тесты с помощью браузера (не будет работать в автономной среде, если мы не используем драйвер HtmlUnit — но это выходит за рамки этой статьи).
Добавление селена
Добавить Selenium к тесту очень просто, но я хотел достичь немного большего, поэтому я создал собственную аннотацию, чтобы пометить свои тесты как тесты Selenium. Я также настроил его так, чтобы он позволял внедрять WebDriver
в тестовый экземпляр:
01
02
03
04
05
06
07
08
09
10
|
@RunWith (SpringJUnit4ClassRunner. class ) @SpringApplicationConfiguration (classes = Application. class ) @WebIntegrationTest (value = "server.port=9000" ) public class HomeControllerTest { @Autowired private WebDriver driver; } |
@SeleniumTest
@SeleniumTest
— это пользовательская аннотация:
01
02
03
04
05
06
07
08
09
10
11
12
13
|
@Documented @Inherited @Retention (RetentionPolicy.RUNTIME) @Target (ElementType.TYPE) @TestExecutionListeners ( listeners = SeleniumTestExecutionListener. class , mergeMode = MERGE_WITH_DEFAULTS) public @interface SeleniumTest { Class<? extends WebDriver> driver() default FirefoxDriver. class ; } |
В аннотации используется прослушиватель выполнения теста, который создаст экземпляр WebDriver
который можно использовать в интеграционном тесте. TestExecutionListener
определяет API слушателя для реакции на события выполнения теста. Это может быть использовано для тестирования инструментов. Примеры реализации в Spring Test используются, например, для поддержки управляемых тестами транзакций или внедрения зависимостей в тестовые экземпляры.
TestExecutionListener
Примечание: некоторые части кода SeleniumTestExecutionListener
пропущены для лучшей читаемости.
SeleniumTestExecutionListener
предоставляет способ внедрить настроенный WebDriver
в тестовые экземпляры. Экземпляр драйвера будет создан только один раз, а используемый драйвер можно просто изменить с @SeleniumTest
аннотации @SeleniumTest
. Самым важным было зарегистрировать драйвер в Bean Factory.
01
02
03
04
05
06
07
08
09
10
11
12
13
|
@Override public void prepareTestInstance(TestContext testContext) throws Exception { ApplicationContext context = testContext.getApplicationContext(); if (context instanceof ConfigurableApplicationContext) { SeleniumTest annotation = findAnnotation( testContext.getTestClass(), SeleniumTest. class ); webDriver = BeanUtils.instantiate(annotation.driver()); // register the bean with bean factory } } |
Перед каждым методом тестирования базовый URL-адрес приложения будет открыт WebDriver
:
1
2
3
4
5
6
7
|
@Override public void beforeTestMethod(TestContext testContext) throws Exception { SeleniumTest annotation = findAnnotation( testContext.getTestClass(), SeleniumTest. class ); webDriver.get(annotation.baseUrl()); } |
Кроме того, при каждом сбое будет создаваться скриншот:
01
02
03
04
05
06
07
08
09
10
11
|
@Override public void afterTestMethod(TestContext testContext) throws Exception { if (testContext.getTestException() == null ) { return ; } File screenshot = ((TakesScreenshot) webDriver).getScreenshotAs(OutputType.FILE); // do stuff with the screenshot } |
После каждого теста водитель будет закрыт:
1
2
3
4
5
6
|
@Override public void afterTestClass(TestContext testContext) throws Exception { if (webDriver != null ) { webDriver.quit(); } } |
Это всего лишь пример. Очень простая реализация. Мы могли бы расширить возможности аннотации и слушателя.
Тест
Выполнение теста ниже запустит браузер Chrome и выполнит несколько простых проверок с Selenium:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
@RunWith (SpringJUnit4ClassRunner. class ) @SpringApplicationConfiguration (classes = Application. class ) @WebIntegrationTest (value = "server.port=9000" ) public class HomeControllerTest { @Autowired private WebDriver driver; private HomePage homePage; @Before public void setUp() throws Exception { homePage = PageFactory.initElements(driver, HomePage. class ); } @Test public void containsActuatorLinks() { homePage.assertThat() .hasActuatorLink( "autoconfig" , "beans" , "configprops" , "dump" , "env" , "health" , "info" , "metrics" , "mappings" , "trace" ) .hasNoActuatorLink( "shutdown" ); } @Test public void failingTest() { homePage.assertThat() .hasNoActuatorLink( "autoconfig" ); } } |
Тест использует простой объект страницы с пользовательскими утверждениями AssertJ. Вы можете найти полный исходный код в GitHub. Смотрите ссылки.
В случае сбоя скриншот, сделанный драйвером, будет сохранен в соответствующей директории.
Резюме
Интеграционное тестирование полностью загруженного приложения Spring Boot возможно в обычном тесте JUnit благодаря аннотациям @WebIntegrationTest
и @SpringApplicationConfiguration
. Запуск приложения в рамках теста открывает возможность нанять Selenium и запустить функциональные тесты с помощью браузера. Если вы объедините его с профилями и некоторыми другими функциями Spring Test (например, @Sql
, @SqlConfig
), вы можете получить довольно мощное, но простое решение для интеграционных тестов.
использованная литература
- Исходный код: https://github.com/kolorobot/spring-boot-thymeleaf
- Весеннее тестирование загрузки: http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-testing
- Весеннее тестирование: http://docs.spring.io/spring/docs/current/spring-framework-reference/html/testing.html
Ссылка: | Весеннее тестирование интеграции с Selenium от нашего партнера JCG Рафаля Боровца в блоге Codeleak.pl . |