Статьи

Весеннее тестирование интеграции загрузки с Selenium

Веб-интеграционные тесты позволяют проводить интеграционное тестирование приложения 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")
@SeleniumTest(driver = ChromeDriver.class, baseUrl = "http://localhost: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;
 
    String baseUrl() default "http://localhost:8080";
}

В аннотации используется прослушиватель выполнения теста, который создаст экземпляр 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")
@SeleniumTest(driver = ChromeDriver.class, baseUrl = "http://localhost: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 ), вы можете получить довольно мощное, но простое решение для интеграционных тестов.

использованная литература