文章目录
- 一、hikari框架的各层次内容解释(导图)
- 二、会用到的测试:Mockito 的 MockMvc
一、hikari框架的各层次内容解释(导图)
以前第一次见到类似的框架还是ruoyi框架。彼时的我还完全不明白这种文件结构,看到就怕。现在做了个思维导图小梳理,图中以我本次课程作业中的entity-Message为例,衍生出相关的controller、dao、service。
二、会用到的测试:Mockito 的 MockMvc
对于其中的各个常用方法进行了梳理。
仅作示例作用,并没有深入解释其原理。
对我帮助很大的Mockito应用指南:https://hezhiqiang8909.gitbook.io/java/docs/javalib/mockito
1. 正向思考简单的Mockito的测试怎么写
此处作为总结,本文后面有一些我遇到的例子。再次说明,仅作示例作用,并未深入解释原理。
- @ExtendWith、@WebMvcTest、@MockBean配置好
- @BeforeEach进行setup,初始化全局变量
- @Test每一个controller类里的方法,初始化局部变量,譬如一些列表、一些到Page的映射
- 对模拟出来的变量配置返回值:given…willReturn 或 when…thenReturn(或 doNothing)。
· 具体语法:when(xxxService的函数(函数调用的参数…)).thenReturn(模拟出来的东西的返回值) - 使用Mockito的 perform…andExpect 进行HTTP连接,测试连接是否200、相应属性是否正确、返回值如何;如有需要,测试能否redirect到下一个指定的网页。
· 具体语法:mockMvc.perform(get(网址).sessionAttrs或params).andExpect(status().isOk()).andExpect(view().name(xxx)).andExpect(model().attribute(expected,actual)) - 使用Mockito的 verify 方法,对于属性和状态进行测试对于前述配置过的函数被调用的次数进行测试。
· 具体语法:verify(xxxService).xxxService刚才调用的函数(函数的参数) - 如果需要,如果被测试类里有 成功vs失败、不重复vs重复、登录与否、更改密码/图片 等等的分类讨论,那么可以考虑写成多个测试类
2. 一个完整测试段落的例子
@Test
void modifyMessage() throws Exception{
// 局部变量初始化
int messageID = 1;
String content = "Updated message content";
Message message = new Message();
message.setContent(content);
message.setTime(LocalDateTime.now());
message.setState(1);
// when...thenReturn
when(messageService.findById(messageID)).thenReturn(message);
// perform...post
mockMvc.perform(post("/modifyMessage.do")
.param("messageID", String.valueOf(messageID))
.param("content", content))
.andExpect(status().isOk())
.andExpect(content().string("true")); // return value
// verify
verify(messageService, times(1)).findById(messageID);
verify(messageService, times(1)).update(message);
// 这里是Junit的测试方法,assertEquals(预期值,实际值)判断两者是否相等
// 体现Junit可以和Mockito结合使用
assertEquals(content, message.getContent());
assertEquals(1, message.getState());
}
3. when...thenReturn
或者given...willReturn
或者doNothing
函数:都用于配置、指定模拟对象的行为,模拟对象应该返回的东西。
// when和given分别来一个例子
when(messageService.findWaitState(message_pageable)).thenReturn(messagePage);
given(venueService.findAll(any())).willReturn(venues);
// 可以做一点小改变
// 自定义 getUniqueId() 的返回值
when(test.getUniqueId()).thenReturn(43);
// 在测试中使用mock对象
assertEquals(test.getUniqueId(), 43);
// doNothing:当 messageService.confirmMessage 方法被调用时,不做任何操作。
doNothing().when(messageService).confirmMessage(id_user_success);
- 用于配置模拟对象的行为,是为了模拟方法的返回值,使得在测试中可以预先定义方法的执行结果,这种方式允许测试特定方法在给定参数下的行为,以便测试代码的其他部分能够按预期工作。
- when: 当调用 findWaitState 方法并传入指定的 message_pageable 对象时(findWaitState),模拟对象应该返回 messagePage 对象。
- given:当调用 venueService.findAll(pageable) 方法时(findAll(any())),模拟对象应该返回 pagedResponse 对象。
3.1 关于其中的findAll
本次课程作业中有一个将结果封装成Page<EntityName>的功能。因此在测试的时候,findAll的作用是根据传入的 pageable 参数,从数据库中查询符合条件的场馆数据,并将结果封装成一个 Page<EntityName> 对象返回,举例:返回Page<Venue>
Page<Venue> findAll(Pageable pageable);
when(venueService.findAll(pageable)).thenReturn(pageOfVenues);
given(venueService.findAll(any())).willReturn(venues);
4. perform
和andExpect
函数:用来模拟http连接的
mockMvc.perform(get("/message_manage"))
.andExpect(status().isOk())
.andExpect(view().name("admin/message_manage"))
.andExpect(model().attribute("total", messagePage.getTotalPages()));
- 使用 mockMvc 执行模拟的 HTTP GET 请求到 /message_manage 路径。
- 预期响应状态status码为 200 (OK),视图view也就是在原有的类里返回的值名称为 admin/message_manage。
- 验证模型中的 total 属性是否等于 messagePage 的总页数,写这句是因为在原有的类里有对model的attribute进行赋值等操作。
5. verify
函数:验证方法是否被调用过、调用过几次
// 举两个例子
verify(messageService, times(/*wanted number of invocations*/1)).findWaitState(message_pageable);
verify(venueService).findAll(pageable);
// 原型可做了解
@CheckReturnValue
public static <T> T verify(T mock, VerificationMode mode) {
return MOCKITO_CORE.verify(mock, mode);
}
-
messageService 是要验证的模拟对象。
-
times(1) 指定了方法 findWaitState 应该被调用的次数,这里是期望被调用一次。
-
后续的findWaitState(message_pageable) 是实际要验证的方法调用,message_pageable 是该方法的参数。
-
这行代码的作用是确保
messageService.findWaitState(message_pageable)
方法被调用了一次。如果调用次数不符合期望,将会抛出验证失败的异常,从而可以确保方法在测试中按预期执行。 -
@CheckReturnValue 注解是用来提示调用者检查方法的返回值,确保返回值不被忽略,它提醒调用该方法后应该考虑处理方法的返回值,例如赋值给变量、进行检查或者使用返回值执行其他操作,以免造成程序逻辑错误或资源泄漏等问题。
6. json相关操作:$[i]
的意思是第i个参数、$
是数组
mockMvc.perform(get("/venueList.do")
.param("page", String.valueOf(page)))
.andExpect(status().isOk())
.andExpect(content().contentType("application/json")) // 表示期望返回的响应内容类型为 application/json,即 JSON 格式
.andExpect(jsonPath("$", hasSize(1))) // 表示期望返回的 JSON 数组 $ 的长度为 1,即结果包含一个元素
.andExpect(jsonPath("$[0].venueID", is(1))) // 表示期望返回的 JSON 数组中第一个元素的 venueID 属性值为 1
7. perform....andExpect
里的is3xxRedirection()
- 去官网查到:判断是不是在org.springframework.http.HttpStatus.Series.REDIRECTION里,也就是3开头的意思是重定向
- (1开头:info,2开头:成功,3开头:重定向,4开头:客户那边的错误,5开头:服务器的错误)
感谢阅读!新人第一篇文章,请指正!