Spring Boot 测试 - 使用 Testcontainers 的 REST API 集成测试

x33g5p2x  于2022-02-16 转载在 Spring  
字(8.7k)|赞(0)|评价(0)|浏览(293)

在本教程中,我们将学习如何使用**Testcontainers进行Spring Boot应用集成测试。

**请查看我的Spring Boot测试Udemy课程。 **Testing Spring Boot Application with JUnit and Mockito (Includes Testcontainers)

首先,我们使用本地MySQL数据库编写集成测试,然后我们将用Testcontainers作为解决方案来解决这个问题。

SpringBoot使用@SpringBootTest注释为集成测试提供了很好的支持。我们可以使用@SpringBootTest注解来加载应用程序上下文并测试各种组件。 

@SpringBootTest将引导完整的应用上下文,这意味着我们可以@Autowire任何被组件扫描到的bean进入我们的测试。

在本教程中,我们将使用@SpringBootTest注解进行集成测试。

使用的工具和技术

  • Java 11+
  • Spring Boot
  • Spring Data JPA
  • MySQL
  • Lombok
  • JUnit 5框架
  • IntelliJ IDEA
  • Testcontainers
  • Docker
  • Maven

什么是集成测试

顾名思义,集成测试的重点是集成应用程序的不同层。这也意味着不涉及嘲弄。

基本上,我们为测试一个可能涉及与多个组件交互的功能而编写集成测试。

例子。完整的**员工管理功能的集成测试(EmployeeRepository, EmployeeService, EmployeeController)。 **

完整的**用户管理功能(UserController, UserService, and UserRepository)的集成测试。 **

完整的**登录功能(LoginRespository、LoginController、Login Service)的集成测试,**等。

开发步骤

  1. 创建Spring Boot应用程序
  2. 配置MySQL数据库
  3. 创建JPA实体
  4. 创建Spring Data JPA存储库
  5. 创建Spring Boot REST控制器
  6. 创建与MySQL数据库的集成测试
  7. 什么是Testcontainers
  8. 在Spring Boot项目中添加测试容器
  9. 使用Testcontainers编写集成测试
  10. 演示

1. 创建Spring Boot应用程序

使用spring initialize,创建一个Spring Boot项目,并添加以下依赖项。

  • Spring Web
  • Spring Data JPA
  • Lombok
  • MySQL驱动
    将Spring boot项目生成一个zip文件,解压后导入IntelliJ IDEA中。

2. 配置MySQL数据库

让我们在这个例子中使用MySQL数据库来存储和检索数据,我们将使用Hibernate属性来创建和删除表。

打开application.properties文件,在其中添加以下配置。

spring.datasource.url=jdbc:mysql://localhost:3306/demo?useSSL=false
spring.datasource.username=root
spring.datasource.password=Mysql@123

spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5InnoDBDialect

spring.jpa.hibernate.ddl-auto = create-drop

确保在运行Spring boot应用程序之前,你将创建一个演示数据库。
同时,根据你机器上的MySQL安装情况,改变MySQL的用户名和密码。

3. 创建JPA实体

接下来,让我们创建一个Student。JPA实体。 

package net.javaguides.spirngboot.entity;

import lombok.*;

import javax.persistence.*;

@Setter
@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Entity
@Table(name = "students")
public class Student {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;

    @Column(name = "first_name")
    private String firstName;

    @Column(name = "last_name")
    private String lastName;
    private String email;
}

4. 创建Spring Data JPA存储库

让我们创建StudentRepository,它扩展了JpaRepository接口。

package net.javaguides.spirngboot.repository;

import net.javaguides.spirngboot.entity.Student;
import org.springframework.data.jpa.repository.JpaRepository;

public interface StudentRepository extends JpaRepository<Student, Long> {
}

5. 创建Spring Boot REST控制器

让我们创建StudentController类并添加这几个REST端点:

package net.javaguides.spirngboot.controller;

import net.javaguides.spirngboot.entity.Student;
import net.javaguides.spirngboot.repository.StudentRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/api/students")
public class StudentController {

    @Autowired
    private StudentRepository studentRepository;

    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    public Student createStudent(@RequestBody Student student){
        return studentRepository.save(student);
    }

    @GetMapping
    public List<Student> getAllStudents(){
        return studentRepository.findAll();
    }
}

6. 创建与MySQL数据库的集成测试

现在,让我们为GET ALL Students REST API创建一个集成JUnit测试。

package net.javaguides.spirngboot;

import net.javaguides.spirngboot.entity.Student;
import net.javaguides.spirngboot.repository.StudentRepository;
import org.hamcrest.CoreMatchers;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;

import java.util.List;

@SpringBootTest
@AutoConfigureMockMvc
class SpringbootTestcontainersDemoApplicationTests {

	@Autowired
	private StudentRepository studentRepository;

	@Autowired
	private MockMvc mockMvc;

	// given/when/then format - BDD style
	@Test
	public void givenStudents_whenGetAllStudents_thenListOfStudents() throws Exception {
		// given - setup or precondition
		List<Student> students =
				List.of(Student.builder().firstName("Ramesh").lastName("faadatare").email("ramesh@gmail.com").build(),
				Student.builder().firstName("tony").lastName("stark").email("tony@gmail.com").build());
		studentRepository.saveAll(students);

		// when - action
		ResultActions response = mockMvc.perform(MockMvcRequestBuilders.get("/api/students"));

		// then - verify the output
		response.andExpect(MockMvcResultMatchers.status().isOk());
		response.andExpect(MockMvcResultMatchers.jsonPath("$.size()", CoreMatchers.is(students.size())));
	}

}

让我们来理解上面的代码。

我们使用@SpringBootTest注解来加载应用程序上下文并测试各种组件。

MockMvc提供对Spring MVC测试的支持。它封装了所有的Web应用Bean,并使它们可用于测试。 @AutoConfigureMockMvc 注解,可以应用于测试类,以启用和配置MockMvc的自动配置。

@Autowired
private MockMvc mockMvc;

MockMvc.perform()方法将调用一个GET请求方法,它返回ResultActions

ResultActions response = mockMvc.perform(MockMvcRequestBuilders.get("/api/students"));

使用这个结果,我们可以对响应有断言期望,比如它的内容、HTTP状态或头。 

andExpect() 将期望所提供的参数。在我们的例子中,我们期望HTTP状态代码和响应中的JSON数组的大小。

// then - verify the output
response.andExpect(MockMvcResultMatchers.status().isOk());
response.andExpect(MockMvcResultMatchers.jsonPath("$.size()", CoreMatchers.is(students.size())));

7. 运行集成测试

我们所写的集成测试有什么问题?

在编写集成测试时,一个常见的问题是对集成测试应该运行的安装组件(例如:MySQL、RabbitMQ)的依赖性。

在我们的案例中,我们的集成测试依赖于 MySQL 数据库。 在集成测试要运行的每台机器上安装特定版本的MySQL数据库需要很多时间。

基本上,我们的集成测试依赖于外部服务(安装MySQL、Rabbit MQ、Redis等)来运行集成测试,那么如何减少这种依赖性--什么将是解决方案。

解决方案是Testcontainers.

8. 什么是Testcontainers?

Testcontainers是一个支持JUnit测试的Java库,为常见的数据库、Selenium网络浏览器或其他可以在Docker容器中运行的东西提供轻量级的、可抛弃的实例。

使用Testcontainers是相当容易的,它让我们有机会在不需要预装组件的情况下创建集成测试。

使用Testcontainers,我们将总是从一个干净的数据库开始,我们的集成测试可以在任何机器上运行。

Testcontainer允许我们在测试中使用Docker容器。因此,我们可以编写依赖外部资源的自足式集成测试。

9. 在Spring Boot项目中添加Testcontainers

打开pom.xml文件,添加以下Testcontainers依赖项。

<dependency>
	<groupId>org.testcontainers</groupId>
	<artifactId>testcontainers</artifactId>
	<version>1.16.2</version>
	<scope>test</scope>
</dependency>
<dependency>
	<groupId>org.testcontainers</groupId>
	<artifactId>junit-jupiter</artifactId>
	<version>1.16.2</version>
	<scope>test</scope>
</dependency>
<dependency>
	<groupId>org.testcontainers</groupId>
	<artifactId>mysql</artifactId>
	<version>1.16.2</version>
	<scope>test</scope>
</dependency>

使用Testcontainers编写集成测试

让我们把集成测试改为使用Testcontainers。

我们将使用Singleton容器模式来使用Testcontainers。

单子容器模式对于定义一个只对几个测试类启动一次的容器是非常有用的。

让我们创建一个基类AbstractContainerBaseTest,这样我们所有的集成测试都可以扩展而不需要重复共同的配置。

package net.javaguides.spirngboot;

import org.testcontainers.containers.MySQLContainer;

public class AbstractContainerBaseTest {

    static final MySQLContainer MY_SQL_CONTAINER;

    static {
        MY_SQL_CONTAINER = new MySQLContainer("mysql:latest");

        MY_SQL_CONTAINER.start();
    }
}

现在,简单地用上述AbstractContainerBaseTest:扩展我们的集成类

package net.javaguides.spirngboot;

import net.javaguides.spirngboot.entity.Student;
import net.javaguides.spirngboot.repository.StudentRepository;
import org.hamcrest.CoreMatchers;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;

import java.util.List;

@SpringBootTest
@AutoConfigureMockMvc
class SpringbootTestcontainersDemoApplicationTests extends AbstractContainerBaseTest{

	@Autowired
	private StudentRepository studentRepository;

	@Autowired
	private MockMvc mockMvc;

	// given/when/then format - BDD style
	@Test
	public void givenStudents_whenGetAllStudents_thenListOfStudents() throws Exception {
		// given - setup or precondition
		List<Student> students =
				List.of(Student.builder().firstName("Ramesh").lastName("faadatare").email("ramesh@gmail.com").build(),
				Student.builder().firstName("tony").lastName("stark").email("tony@gmail.com").build());
		studentRepository.saveAll(students);

		// when - action
		ResultActions response = mockMvc.perform(MockMvcRequestBuilders.get("/api/students"));

		// then - verify the output
		response.andExpect(MockMvcResultMatchers.status().isOk());
		response.andExpect(MockMvcResultMatchers.jsonPath("$.size()", CoreMatchers.is(students.size())));
	}

}

10. 演示

在运行集成测试之前,确保Docker在你的机器上运行,否则,你将无法运行集成测试。

下面是使用Testcontainers进行的上述集成测试的输出。

GitHub存储库

本教程的完整源代码在我的GitHub仓库https://github.com/RameshMF/springboot-testcontainers-demo

相关文章