说明
基于springboot test 单元测试实例。
依赖引入
<dependencies>
<!--单元测试-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
@WebMvcTest 是 Spring Boot Test 框架中的一个注解,用于针对 Spring MVC 应用程序的单元测试。它提供了一个轻量级的方式来测试控制器类,而无需启动完整的应用程序上下文。
@WebMvcTest 的作用是:
- 限制测试范围:它只加载和构建与 Spring MVC 相关的组件,例如控制器、异常处理器、拦截器等。这样可以减少测试所需的资源和时间,使测试更加快速和高效。
- 提供便捷的模拟请求和验证响应的方法:它通过 MockMvc 提供了模拟请求和验证响应的功能。你可以使用 MockMvc 来发送各种类型的 HTTP 请求(GET、POST、PUT、DELETE 等)并验证返回的响应。
@WebMvcTest 的一般用法如下:
- 在测试类上添加 @RunWith(SpringRunner.class) 注解以指定使用 Spring Runner 来运行测试。
- 使用 @WebMvcTest 注解标记测试类,并指定要测试的控制器类。例如,@WebMvcTest(UserController.class) 将测试 UserController 类。
- 使用 @Autowired 注解注入 MockMvc 实例,以便在测试中使用。
- 使用 @MockBean 注解创建模拟的依赖(例如服务、存储库等),以便在测试中模拟它们的行为。
- 在测试方法中使用 MockMvc 发送请求并验证响应。你可以使用 perform() 方法来发送各种类型的请求,例如 get()、post()、put() 、delete() 等。然后,使用 andExpect() 方法来验证响应的状态码、内容类型、响应体等。
下面是一个示例:
Service及Controller代码:
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable {
private Long id;
private String name;
}
@Service
public class UserService {
public User getUserById(Long id) {
// 根据id从数据库或其他数据源获取用户信息
// 省略具体实现
return new User(id, "John Doe");
}
}
@RestController
@RequestMapping("/api")
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/users/{id}")
public ResponseEntity<User> getUserById(@PathVariable Long id) {
User user = userService.getUserById(id);
return ResponseEntity.ok(user);
}
}
单元测试示例:
//@RunWith(SpringRunner.class) //junit5.x不需要添加
@WebMvcTest(UserController.class)
public class UserControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private UserService userService;
@Test
public void testGetUserById() throws Exception {
// 1.模拟userService的返回值
User mockUser = new User(1L, "Mock User");
// 2.使用 Mockito.when() 方法模拟了 userService.getUserById() 方法的行为,并返回了预期的 User 对象。
Mockito.when(userService.getUserById(1L)).thenReturn(mockUser);
// 3.发起GET请求,验证返回结果
mockMvc.perform(get("/api/users/{id}", 1L))
.andExpect(status().isOk())
.andExpect(jsonPath("$.id").value(1L))
.andExpect(jsonPath("$.name").value("Mock User"));
// 4.验证userService的方法是否被调用
Mockito.verify(userService, Mockito.times(1)).getUserById(1L);
Mockito.verifyNoMoreInteractions(userService);
}
}
TestRestTemplate 是 Spring Framework 提供的一个用于进行集成测试的工具类,它是 RestTemplate 的一个特殊子类。 它提供了一组方便的方法,可以用于发送 HTTP 请求、接收响应,并进行断言和验证。
TestRestTemplate 的主要作用是在单元测试或集成测试中模拟客户端与 RESTful API 之间的交互。 通过使用 TestRestTemplate,你可以在测试中发送 HTTP 请求,获取响应,并对响应进行断言和验证,从而验证你的 API 的行为是否符合预期。
以下是 TestRestTemplate 的一些常见用法:
- 发送 HTTP 请求:使用 getForEntity()、postForEntity()、put()、delete() 等方法发送 GET、POST、PUT、DELETE 等类型的请求。
- 接收响应:TestRestTemplate 的请求方法返回一个 ResponseEntity 对象,其中包含响应的状态码、响应头和响应体。
- 断言和验证:使用断言方法(如 assertThat())来验证响应的状态码、响应体的内容、响应头等。
- 设置请求参数和请求头:使用 exchange() 方法可以自定义请求参数、请求头和响应类型等。
- 模拟请求和响应:TestRestTemplate 可以与其他测试工具(如 MockMvc)结合使用,以模拟请求和响应,进行更加复杂的测试场景。
以下是示例:
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) // 启动一个嵌入式的随机端口的服务器,并加载应用程序上下文
public class UserControllerTest2 {
@Autowired
private TestRestTemplate restTemplate;
/**
* 要启动项目,可以通过 UnittestJunitApplication.java 类启动
*/
@Test
public void testGetUserById() {
ResponseEntity<User> response = restTemplate.getForEntity("/api/users/{id}", User.class, 1L);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(response.getBody()).isNotNull();
User user = response.getBody();
assertThat(user.getId()).isEqualTo(1L);
assertThat(user.getName()).isEqualTo("John Doe");
}
@MockBean
private UserService userService;
/**
* 不需要启动项目
* restTemplate 搭配 MockMvc、@MockBean、Mockito 等,来模拟依赖、验证方法调用和设置方法的行为。
*/
@Test
public void testGetUserByIdV2() {
// 模拟 userService.getUserById() 方法的行为
User user = new User(1L, "John");
Mockito.when(userService.getUserById(1L)).thenReturn(user);
// 发送 GET 请求
ResponseEntity<User> response = restTemplate.getForEntity("/api/users/{id}", User.class, 1L);
// 验证响应
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(response.getBody()).isNotNull();
User responseBody = response.getBody();
assertThat(responseBody.getId()).isEqualTo(1L);
assertThat(responseBody.getName()).isEqualTo("John");
// 验证 userService.getUserById() 方法是否被调用
Mockito.verify(userService, Mockito.times(1)).getUserById(1L);
Mockito.verifyNoMoreInteractions(userService);
}
}
引入依赖
<dependencies>
<!-- spring-boot-starter-test 单元测试-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- 使用 spring-boot-starter-test 模块来实现自动化的数据库测试-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- 在单元测试中使用嵌入式数据库 -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
简单示例
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
// 省略构造函数、getter 和 setter 方法
}
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
// 可以定义其他自定义查询方法
}
单元测试类:
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager;
import static org.assertj.core.api.Assertions.assertThat;
@DataJpaTest
public class UserRepositoryTest {
@Autowired
private TestEntityManager entityManager;
@Autowired
private UserRepository userRepository;
@Test
public void testSaveUser() {
// 创建一个用户对象
User user = new User();
user.setName("John");
// 保存用户到数据库
User savedUser = userRepository.save(user);
// 从数据库中查询用户
User retrievedUser = entityManager.find(User.class, savedUser.getId());
// 验证用户是否保存成功
assertThat(retrievedUser).isNotNull();
assertThat(retrievedUser.getName()).isEqualTo("John");
}
}
在这个示例中,我们使用了 @DataJpaTest 注解来创建一个嵌入式的数据库,并自动加载相关的实体类和存储库。
在 testSaveUser() 方法中,我们创建了一个 User 对象并保存到数据库。 然后,我们使用 TestEntityManager(提供了对嵌入式数据库的访问)来从数据库中查询保存的用户。最后,我们使用断言来验证用户是否保存成功。
一个复杂示例:
假设你有一个简单的博客应用程序,其中包含两个实体类:Post 和 Comment。 Post 表示博客文章,Comment 表示对博客文章的评论。它们之间是一对多的关系。
首先,定义实体类和相关的存储库接口:
// Post.java
@Entity
public class Post {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
@OneToMany(mappedBy = "post", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Comment> comments = new ArrayList<>();
// 省略构造函数、getter 和 setter 方法
}
// Comment.java
@Entity
public class Comment {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String content;
@ManyToOne(fetch = FetchType.LAZY)
private Post post;
// 省略构造函数、getter 和 setter 方法
}
// PostRepository.java
@Repository
public interface PostRepository extends JpaRepository<Post, Long> {
// 可以定义其他自定义查询方法
}
// CommentRepository.java
@Repository
public interface CommentRepository extends JpaRepository<Comment, Long> {
// 可以定义其他自定义查询方法
}
单元测试类:
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
@DataJpaTest
public class BlogIntegrationTest {
@Autowired
private TestEntityManager entityManager;
@Autowired
private PostRepository postRepository;
@Autowired
private CommentRepository commentRepository;
@Test
public void testSavePostWithComments() {
// 创建一个博客文章对象
Post post = new Post();
post.setTitle("Hello, World!");
// 创建两个评论对象
Comment comment1 = new Comment();
comment1.setContent("Great post!");
comment1.setPost(post);
Comment comment2 = new Comment();
comment2.setContent("Nice article!");
comment2.setPost(post);
// 添加评论到博客文章
post.getComments().add(comment1);
post.getComments().add(comment2);
// 保存博客文章到数据库
postRepository.save(post);
// 从数据库中查询保存的博客文章和评论
Post savedPost = entityManager.find(Post.class, post.getId());
List<Comment> savedComments = commentRepository.findAll();
// 验证博客文章和评论是否保存成功
assertThat(savedPost).isNotNull();
assertThat(savedPost.getTitle()).isEqualTo("Hello, World!");
assertThat(savedComments).hasSize(2);
assertThat(savedComments.get(0).getContent()).isEqualTo("Great post!");
assertThat(savedComments.get(1).getContent()).isEqualTo("Nice article!");
}
}
在这个示例中,我们使用了 @DataJpaTest 注解来创建一个嵌入式的数据库,并自动加载相关的实体类和存储库。
在 testSavePostWithComments() 方法中,我们创建了一个 Post 对象和两个相关的 Comment 对象,并将评论关联到博客文章。 然后,我们保存博客文章到数据库,并使用 TestEntityManager 和存储库接口来从数据库中查询保存的博客文章和评论。最后,我们使用断言来验证数据是否保存成功。