在本教程中,我们将学习如何使用**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
注解进行集成测试。
顾名思义,集成测试的重点是集成应用程序的不同层。这也意味着不涉及嘲弄。
基本上,我们为测试一个可能涉及与多个组件交互的功能而编写集成测试。
例子。完整的**员工管理功能的集成测试(EmployeeRepository, EmployeeService, EmployeeController)。 **
完整的**用户管理功能(UserController, UserService, and UserRepository)的集成测试。 **
完整的**登录功能(LoginRespository、LoginController、Login Service)的集成测试,**等。
使用spring initialize,创建一个Spring Boot项目,并添加以下依赖项。
让我们在这个例子中使用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的用户名和密码。
接下来,让我们创建一个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;
}
让我们创建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> {
}
让我们创建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();
}
}
现在,让我们为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())));
在编写集成测试时,一个常见的问题是对集成测试应该运行的安装组件(例如:MySQL、RabbitMQ)的依赖性。
在我们的案例中,我们的集成测试依赖于 MySQL 数据库。 在集成测试要运行的每台机器上安装特定版本的MySQL数据库需要很多时间。
基本上,我们的集成测试依赖于外部服务(安装MySQL、Rabbit MQ、Redis等)来运行集成测试,那么如何减少这种依赖性--什么将是解决方案。
解决方案是Testcontainers.。
Testcontainers是一个支持JUnit测试的Java库,为常见的数据库、Selenium网络浏览器或其他可以在Docker容器中运行的东西提供轻量级的、可抛弃的实例。
使用Testcontainers是相当容易的,它让我们有机会在不需要预装组件的情况下创建集成测试。
使用Testcontainers,我们将总是从一个干净的数据库开始,我们的集成测试可以在任何机器上运行。
Testcontainer允许我们在测试中使用Docker容器。因此,我们可以编写依赖外部资源的自足式集成测试。
打开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。
我们将使用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())));
}
}
在运行集成测试之前,确保Docker在你的机器上运行,否则,你将无法运行集成测试。
下面是使用Testcontainers进行的上述集成测试的输出。
本教程的完整源代码在我的GitHub仓库https://github.com/RameshMF/springboot-testcontainers-demo。
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
内容来源于网络,如有侵权,请联系作者删除!