网站首页 > 技术文章 正文
什么是单元测试?
单元测试是一种软件测试,重点验证软件应用程序的各个独立组件或单元的正确性。这些单元通常由代码库中的各个函数、方法或类组成。单元测试的主要目的是确保每个代码单元按预期运行并为给定的输入集生成正确的输出。
单元是测试驱动开发 (TDD) 方法的一个组成部分,开发人员在编写代码本身之前先编写测试。编写单元测试有助于设计具有清晰和模块化结构的软件,使其更易于维护,并随着时间的推移进行扩展。
什么是WebTestClient?
WebTestClient 是 Spring 框架测试基础设施的一部分,专门用于测试使用 Spring WebFlux 构建的应用程序,Spring WebFlux 是用于构建 Web 应用程序的响应式编程框架。可以使用 WebTestClient 为 Spring WebFlux 控制器和处理程序编写单元和集成测试。
单元测试
@WebFluxTest(controllers = TodoController.class)
@AutoConfigureWebTestClient
public class TodoControllerUnitTest {
private final WebTestClient webTestClient;
@MockBean
private final TodoService todoService;
private final String url = "/api/v1/todos";
@Autowired
public TodoControllerUnitTest(WebTestClient webTestClient, TodoService todoService) {
this.webTestClient = webTestClient;
this.todoService = todoService;
}
.
.
.
.
// 测试方法
}
@WebFluxTest(controllers = TodoController.class):
- @WebFluxTest是一个 Spring Boot 测试注解,指示此类是对 WebFlux 控制器的测试。
- controllers是一个属性,指定此测试重点关注哪个控制器。在本例中,它正在测试该类TodoController。
@AutoConfigureWebTestClient:
- @AutoConfigureWebTestClient是另一个 Spring Boot 测试注解,配置WebTestClient用于测试。WebTestClient是 Spring 提供的客户端,用于在测试期间向 Web 应用程序发出 HTTP 请求。
- 它通常用于测试应用程序的端点、发送 HTTP 请求并验证响应。
@MockBean:
- @MockBean用于创建 bean 的Mock以进行测试。在本例中,将创建TodoService bean 的Mock。
- 该TodoService bean 通常是处理与待办事项相关的业务逻辑的服务,并且它测试时会被替换为Mock实例。这可以控制测试期间TodoService的行为。
1)测试findTodoById 方法
@GetMapping("/{id}")
@Operation(summary = "findTodoById")
Mono<ResponseEntity<Todo>> findTodoById(@PathVariable int id) {
return todoService.findTodoById(id)
.map(todo -> ResponseEntity.ok(todo));
}
@Test
void findTodoById_test_should_return_todo_object_and_status_should_be_ok() {
var todo = Todo.builder().id(1).title("test title 1").description("test description 1").build();
when(todoService.findTodoById(todo.getId())).thenReturn(Mono.just(todo));
String findTodoByIdUrl = String.format("%s/%s", url, todo.getId());
webTestClient
.get()
.uri(findTodoByIdUrl)
.exchange()
.expectStatus()
.is2xxSuccessful()
.expectBody(Todo.class)
.consumeWith(result -> {
var existTodo = result.getResponseBody();
assert existTodo != null;
assertEquals(todo.getId(), existTodo.getId());
assertEquals(todo.getTitle(), existTodo.getTitle());
});
}
@Test
void findTodoById_test_should_return_error_response_object_and_status_should_be_not_found() {
var todo = Todo.builder().id(3).title("test title 3").description("test description 3").build();
when(todoService.findTodoById(todo.getId())).thenReturn(Mono.error(new TodoNotFoundException(String.format("Todo not found. ID: %s", todo.getId()))));
String findTodoByIdUrl = String.format("%s/%s", url, todo.getId());
webTestClient
.get()
.uri(findTodoByIdUrl)
.exchange()
.expectStatus()
.isNotFound()
.expectBody(ErrorResponse.class)
.consumeWith(result -> {
var errorResponse = result.getResponseBody();
assertEquals(false, errorResponse.isSuccess());
assertEquals("Todo not found. ID: 3", errorResponse.getMessage());
});
}
2)测试findAllTodos 方法
@GetMapping("")
@ResponseStatus(HttpStatus.OK)
@Operation(summary = "查找所有Todos")
Flux<Todo> findAllTodos() {
return todoService.findAllTodos().log();
}
@Test
void findAllTodos_test_should_return_todo_list_and_status_should_be_ok() {
var todos = List.of(
Todo.builder().id(1).title("test title 1").description("test description 1").build(),
Todo.builder().id(2).title("test title 2").description("test description 2").build(),
Todo.builder().id(3).title("test title 3").description("test description 3").build()
);
when(todoService.findAllTodos()).thenReturn(Flux.fromIterable(todos));
webTestClient
.get()
.uri(url)
.exchange()
.expectStatus()
.is2xxSuccessful()
.expectBodyList(Todo.class)
.hasSize(3);
}
3)测试saveTodo方法
@PostMapping("")
@ResponseStatus(HttpStatus.CREATED)
@Operation(summary = "创建Todo")
Mono<ResponseEntity<Todo>> saveTodo(@RequestBody Todo todo) {
return todoService.saveTodo(todo)
.map(savedTodo -> ResponseEntity.status(HttpStatus.CREATED).body(savedTodo));
}
@Test
void saveTodo_test_should_return_saved_todo_and_status_should_be_created() {
var todo = Todo.builder().id(1).title("test title 1").description("test description 1").build();
when(todoService.saveTodo(isA(Todo.class))).thenReturn(Mono.just(todo));
webTestClient
.post()
.uri(url)
.bodyValue(todo)
.exchange()
.expectStatus()
.isCreated()
.expectBody(Todo.class)
.consumeWith(result -> {
var savedTodo = result.getResponseBody();
assert savedTodo != null;
assertEquals(todo.getId(), savedTodo.getId());
assertEquals(todo.getTitle(), savedTodo.getTitle());
});
}
4)测试deleteTodoById方法
@DeleteMapping("/{id}")
@ResponseStatus(HttpStatus.NO_CONTENT)
@Operation(summary = "根据id删除Todo")
Mono<Void> deleteTodoById(@PathVariable int id) {
return todoService.deleteTodoById(id);
}
@Test
void deleteTodoById_test_should_return_empty_and_status_should_be_no_content() {
int todoId = 3;
when(todoService.deleteTodoById(todoId)).thenReturn(Mono.empty());
String deleteUrl = String.format("%s/%s", url, todoId);
webTestClient
.delete()
.uri(deleteUrl)
.exchange()
.expectStatus()
.isNoContent();
}
运行所有测试并查看结果。
猜你喜欢
- 2024-10-17 使用 Spock 编写高效简洁的单元测试
- 2024-10-17 程序员有福了!万字长文带你掌握SpringBoot所提供的测试解决方案
- 2024-10-17 如何使用Spring Boot提供的测试工具和注解。
- 2024-10-17 单元测试实践(Spring-boot+Junbit5+Mockito)
- 2024-10-17 Spring云原生实战指南:8 弹性和可扩展性
- 2024-10-17 SpringBoot 太强了,这些优势你需要了解
- 2024-10-17 如何使用Spring Cloud Contract(如何使用朋友的山姆卡)
- 2024-10-17 Spring Boot 开发离不开这些注解,快来学习啦!
- 2024-10-17 Spring Boot中文参考指南46.3.11、自动配置的Spring WebFlux测试
- 2024-10-17 一台不容错过的Java单元测试代码“永动机”
- 最近发表
- 标签列表
-
- cmd/c (64)
- c++中::是什么意思 (83)
- 标签用于 (65)
- 主键只能有一个吗 (66)
- c#console.writeline不显示 (75)
- pythoncase语句 (81)
- es6includes (73)
- sqlset (64)
- windowsscripthost (67)
- apt-getinstall-y (86)
- node_modules怎么生成 (76)
- chromepost (65)
- c++int转char (75)
- static函数和普通函数 (76)
- el-date-picker开始日期早于结束日期 (70)
- localstorage.removeitem (74)
- vector线程安全吗 (70)
- & (66)
- java (73)
- js数组插入 (83)
- linux删除一个文件夹 (65)
- mac安装java (72)
- eacces (67)
- 查看mysql是否启动 (70)
- 无效的列索引 (74)