使用Spring Boot和HTMLUnit进行端到端测试入门

首先

我是石田,负责圣诞节前夜的圣诞节降临日历,我在Mikatus株式会社担任应用开发工程师,使用Spring Boot 2写代码。

我正在负责的微服务中,没有端到端测试的机制,因此我决定引入它!目前正在进行调查。我希望能够分享在此过程中学到的知识,所以如果你打算在Spring中引入端到端的机制,可以参考一下这个作为第一步。

由于我仍然是个初学者,对于Spring来说还有很多不足之处和需要修正的地方,如果你能告诉我,我会非常高兴。

E2E 测试是指端到端测试 .

请查看这篇文章。

HTMLUnit是什么?

HTMLUnit是一款无GUI的浏览器,通过使用HTMLUnit,可以在虚拟浏览器上对HTML文档进行结构化处理。
另外,HTMLUnit还提供了用于操作结构化DOM的各种操作API(如向表单输入数据、点击链接、提交等),它是一个与JavaScript兼容性也很高的由Java编写的库。

根据我所了解,HTMLUnit本身并不提供测试功能,通常会与JUnit等测试框架结合在一起,用于E2E测试阶段。可以通过模拟浏览器进行测试,目前支持的浏览器包括Chrome、Firefox和Internet Explorer。

通过与Spring结合,可以自动验证以下测试用例:
1. 在表单的文本区域内输入姓名。
2. 提交表单。
3. 确认提交后,输入的姓名会在输出区域中显示。

这个库已经在Spring Framework 4中集成了。

HTMLUnit 是一个基于 Java 的开源浏览器自动化框架。您可以使用它来模拟网页加载和交互操作,非常适用于Web应用程序的功能测试和爬虫开发。HTMLUnit 提供了丰富的API,可以方便地操作网页元素、执行JavaScript代码以及获取和修改页面内容。使用HTMLUnit,您可以在不打开实际浏览器的情况下进行各种网页操作,从而提高效率和可靠性。无论是新手还是经验丰富的开发者,HTMLUnit 都是一个强大且灵活的工具,可以帮助您更轻松地处理Web应用程序的自动化任务。

MockMvc 是什么?

在使用Spring MVC项目构建的应用程序中,进行E2E测试时,另一个重要的关键词是MockMvc。这是Spring Test项目中包含的功能,它是与Spring MVC框架集成的状态下进行测试所必不可少的机制。通过使用MockMvc,我们可以模拟Spring MVC的行为,而不需要在应用程序服务器上进行部署,从而省去了准备服务器的麻烦,并且能够快速进行测试。

スクリーンショット 2019-12-23 23.21.34.png

(1)测试用例方法是为DispatcherServlet设置请求数据(例如请求路径和请求参数)。
(2)MOckMvc对DispatcherServlet进行模拟请求。实际使用的DispatcherServlet被扩展为org.springframework.test.web.servlet.TestDispatcherServlet,用于测试。
(3)DispatcherServlet(Spring MVC的框架处理)调用与请求内容匹配的处理器(控制器)方法。
(4)测试用例方法接收MockMvc返回的执行结果,并验证执行结果的有效性。

春天全面初级入门-利用Spring Framework开发Java应用程序 引用

在生产环境中,通常情况下,Tomcat的Coyote和Catarina组件的类会接受客户端的请求,并将处理任务转交给Dispatcher Servlet。但是,现在Tomcat的功能已经消失了。

而且,以下网站上还进行了详细解释。
10.2.4. 如何使用在单元测试中使用的开源库。

运动环境

    • Spring Boot 2.2.0

 

    JDK 1.8.0_211

情景

我們將進行實作,預計在以下情境中實現:確認在訊息輸入畫面輸入的文本能正確顯示在訊息確認畫面上。

实施

构建.gradle文件的配置

以下是构成如下。

plugins {
    id 'org.springframework.boot' version '2.2.0.RELEASE'
    id 'io.spring.dependency-management' version '1.0.8.RELEASE'
    id 'java'
}

apply plugin: 'io.spring.dependency-management'

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
    testImplementation('org.springframework.boot:spring-boot-starter-test') {
        exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
    }
    compile('org.springframework.boot:spring-boot-starter-actuator')
    compile('org.springframework.boot:spring-boot-starter-web')
    runtime('org.springframework.boot:spring-boot-devtools')
    compile('org.springframework.boot:spring-boot-starter-cache')
    compile group: 'net.sourceforge.htmlunit', name: 'htmlunit', version: '2.36.0'
    compile group: 'org.w3c.css', name: 'sac', version: '1.3'
}

test {
    useJUnitPlatform()
}

控制器的实现

package com.example.demo.web;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;

@Controller
@RequestMapping("/message")
public class MessageController {

    // メッセージ入力 Form の表示
    @RequestMapping(value = "/showForm", method = RequestMethod.GET)
    public String showForm() {
        // message-form.html(メッセージ入力画面)を表示
        return "message-form";
    }

    // メッセージ確認画面の表示
    @RequestMapping(value = "/postMessage", method = RequestMethod.POST)
    public String postMessage(@RequestParam("message") final String message, final Model model) {
        // "message" という attributeName に対して post されてきた message の内容を model#addAttribute でマッピング
        model.addAttribute("message", message);
        // message-confirm.html(メッセージ確認画面)を表示
        return "message-confirm";
    }

}

创建模板文件。

信息输入表单模板

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">

<head>
    <meta charset="UTF-8" />
</head>

<body>
    <h1>メッセージ入力画面</h1>
    <form action="#" th:action="@{/message/processForm}" method="post">
        Message: <input type="text" value="message" id="message" name="message" />
        <input type="submit" />
    </form>
</body>

</html>

消息确认模板

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">

<head>
    <meta charset="UTF-8" />
</head>

<body>
    <h1>メッセージ確認画面</h1>
    <span th:text="${message}" id="received"></span>
</body>

</html>

创建测试类

请查看代码中的解释以获取相关信息。

package com.example.demo;

import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.html.HtmlForm;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
import com.gargoylesoftware.htmlunit.html.HtmlSubmitInput;
import com.gargoylesoftware.htmlunit.html.HtmlTextInput;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.web.servlet.htmlunit.MockMvcWebClientBuilder;
import org.springframework.web.context.WebApplicationContext;

import static org.junit.jupiter.api.Assertions.assertEquals;

@ExtendWith(SpringExtension.class) // (1)
@SpringBootTest // (2)
public class MessageControllerTest {

    @Autowired
    private WebApplicationContext webApplicationContext; // (3)

    private WebClient webClient; 

    @BeforeEach
    public void setup() {
        webClient = MockMvcWebClientBuilder.webAppContextSetup(webApplicationContext).build(); // (4)
    }

    @Test
    public void givenAMessage_whenSent_thenItShows() throws Exception {
        String text = "Hello world!";
        String url = "http://localhost/message/showForm";

        // メッセージ入力画面( message-form.html )を取得
        HtmlPage page = webClient.getPage(url);

        // メッセージ入力画面の id = "message" の DOM 取得
        HtmlTextInput messageText = page.getHtmlElementById("message");

        // 取得した DOM(今回の場合だと input タグ)に "Hello world!" をセット
        messageText.setValueAttribute(text);

        // メッセージ入力画面の form を取得
        HtmlForm form = page.getForms().get(0);

        // メッセージ入力画面の submit を実行( MessageController#postMessage にリクエスト)
        HtmlSubmitInput submit =
                form.getOneHtmlElementByAttribute("input", "type", "submit");
        HtmlPage newPage = submit.click();

        // メッセージ確認画面( message-confirm.html )の id = "received" の DOM からテキストデータを取得
        String receivedText = newPage.getHtmlElementById("received").getTextContent();

        assertEquals(receivedText, text);
    }

}
    (1) @ExtendWith(SpringExtension.class) について

SpringExtension.class 是从 Spring 5 开始提供的一个类,用于使 Spring TestContext Framework 能够在 JUnit 5 平台上运行。
另外,Spring TestContext Framework 是包含在 Spring Test 项目中的,可以在 JUnit、TestNG 等测试框架上使用 Spring 提供的注解、Java 标准注解和 Spring Test 提供的注解等(其他可用功能,请参考此处)。
此外,@ExtendWith 是在 JUnit 5 中提供的注解。有关 JUnit 5 的功能,请在此处查看。

我理解这句话是指在 Junit 5 平台上,使用 Spring 构建的 Web 应用程序的测试中,将使用各种必要功能的定义。

    (2) @SpringBootTest について

通过添加此注释,可以在Spring Boot环境中执行测试。有关与Spring TestContext Framework配置的区别,请参见这里 。

(我猜)这个注释会自动定义模板引擎的前缀和编码等设置。

另外,在只由控制器组成的情况下,可以使用@WebMvcTest。使用此注解可以缩短测试执行时间,因为它会缩小自动扫描的目标类范围。请参考此注解的详细说明。

    (3) WebApplicationContext について

我希望您能理解,在此我们将 DI 容器注入到外部,以使其在接下来的(4)中可以使用。

    (4) MockMvcWebClientBuilder について

这是一个用于生成依赖于MockMvc的HtmlUnit WebClient实例的构建器。在这里,我们认识到它是将DI容器中注册的各种实例与MockMvc和HtmlUnit WebClient集成在一起,以便能够使用它们进行页面获取和其他操作。接下来,我们将使用这个实例进行页面获取和其他操作。

关于未来

    • Web API として利用されることを想定した E2E テストの手法を纏める

 

    Selenium WebDriver を用いた E2E テストの手法を纏める

请参考这些资料
参考这些资料
请参考参考资料
请参阅这些资料
请查阅这些资料
请参考相关资料

JUnit5 @RunWith
通过SpringExtension类的javadoc
Spring 5的Spring Test公开版本
JUnit 5用户指南
Spring Web Contexts
从E2E测试中学到的知识
HtmlUnit介绍
HtmlUnit
10.2.4. 使用OSS库进行单元测试的注解类型SpringBootTest的javadoc
Spring彻底入门,使用Spring Framework开发Java应用程序
Spring MVC Test HtmlUnit

广告
将在 10 秒后关闭
bannerAds