赞
踩
spring-boot-test是SpringBoot的一个功能特性,对众多的单元测试技术进行了集成,我们可以通过在项目中添加下面的依赖引入这项特性:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
其中exclusions的目的只是为了排除junit4而采用Junit5,可根据自己使用的junit版本来确认是否要配置这一项。
当我们依赖了spring-boot-starter-test之后,会自动的引入以下几种常见的单元测试技术:
本篇会对常用的功能进行讲解,如果想了解所有功能,请来这里:【spring-boot-test官网】
对于普通的JAVA代码的测试,通过Junit和Mockito就可以完成,这里我们看看怎么测试spring容易中的Bean,比如:Service。
Service代码:
package com.firewolf.busi.example.springtest; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.StringUtils; import javax.annotation.Resource; import java.util.List; @Service public class UserService { @Resource private UserMapper userMapper; public void addUser(User user) { if (StringUtils.isEmpty(user.getAccount()) || user.getAccount().length() < 4) { throw new RuntimeException("account is too short"); } int i = userMapper.selectCount(User.builder().account(user.getAccount()).build()); if (i > 0) { throw new RuntimeException("account can not repeat"); } userMapper.insertSelective(user); } public List<User> selectUser() { return userMapper.selectAll(); } } ....
我们现在想测试addUser方法的逻辑是否正确,而我们又不需要访问数据库,这个时候,我们可以Mock出UserMapper,来测试addUser的逻辑
测试代码:
@SpringBootTest class UserServiceTest { @Autowired private UserService userService; //我们不关心UserMapper的逻辑,直接mock出来 @MockBean private UserMapper userMapper; @Test void addUser() { assertThrows(RuntimeException.class, () -> userService.addUser(User.builder().name("周扒皮").password("1111").build()), "account is too short"); assertThrows(RuntimeException.class, () -> userService.addUser(User.builder().account("abc").name("周扒皮").password("1111").build()), "account is too short"); String name = "myname"; when(userMapper.selectCount(eq(User.builder().account(name).build()))).thenReturn(1); assertThrows(RuntimeException.class, () -> userService.addUser(User.builder().account(name).build())); assertDoesNotThrow(() -> userService.addUser(User.builder().account("wangba").build())); }
如果这个UserMapper不是mock出来的,那么,就会走自己的真实逻辑。
可以看到,其他的地方的写法,和junit是类似的。
有时候,我们需要测试我们的web controller,来判断我们的请求是否能正常处理
//根据uri模板和uri变量值得到一个GET请求方式的MockHttpServletRequestBuilder; MockHttpServletRequestBuilder get(String urlTemplate, Object... urlVariables) //同get类似,但是是POST方法; MockHttpServletRequestBuilder post(String urlTemplate, Object... urlVariables) //同get类似,但是是PUT方法; MockHttpServletRequestBuilder put(String urlTemplate, Object... urlVariables) //同get类似,但是是DELETE方法; MockHttpServletRequestBuilder delete(String urlTemplate, Object... urlVariables) //同get类似,但是是OPTIONS方法; MockHttpServletRequestBuilder options(String urlTemplate, Object... urlVariables) //提供自己的Http请求方法及uri模板和uri变量,如上API都是委托给这个API; MockHttpServletRequestBuilder request(HttpMethod httpMethod, String urlTemplate, Object... urlVariables) //提供文件上传方式的请求,得到MockMultipartHttpServletRequestBuilder; MockMultipartHttpServletRequestBuilder fileUpload(String urlTemplate, Object... urlVariables) //创建一个从启动异步处理的请求的MvcResult进行异步分派的RequestBuilder; RequestBuilder asyncDispatch(final MvcResult mvcResult)
//:添加头信息; MockHttpServletRequestBuilder header(String name, Object... values)/MockHttpServletRequestBuilder headers(HttpHeaders httpHeaders) //:指定请求的contentType头信息; MockHttpServletRequestBuilder contentType(MediaType mediaType) //:指定请求的Accept头信息; MockHttpServletRequestBuilder accept(MediaType... mediaTypes)/MockHttpServletRequestBuilder accept(String... mediaTypes) //:指定请求Body体内容; MockHttpServletRequestBuilder content(byte[] content)/MockHttpServletRequestBuilder content(String content) //:请求传入参数 MockHttpServletRequestBuilder param(String name,String... values) //:指定请求的Cookie; MockHttpServletRequestBuilder cookie(Cookie... cookies) //:指定请求的Locale; MockHttpServletRequestBuilder locale(Locale locale) //:指定请求字符编码; MockHttpServletRequestBuilder characterEncoding(String encoding) //:设置请求属性数据; MockHttpServletRequestBuilder requestAttr(String name, Object value) //:设置请求session属性数据; MockHttpServletRequestBuilder sessionAttr(String name, Object value)/MockHttpServletRequestBuilder sessionAttrs(Map<string, object=""> sessionAttributes) //指定请求的flash信息,比如重定向后的属性信息; MockHttpServletRequestBuilder flashAttr(String name, Object value)/MockHttpServletRequestBuilder flashAttrs(Map<string, object=""> flashAttributes) //:指定请求的Session; MockHttpServletRequestBuilder session(MockHttpSession session) // :指定请求的Principal; MockHttpServletRequestBuilder principal(Principal principal) //:指定请求的上下文路径,必须以“/”开头,且不能以“/”结尾; MockHttpServletRequestBuilder contextPath(String contextPath) //:请求的路径信息,必须以“/”开头; MockHttpServletRequestBuilder pathInfo(String pathInfo) //:请求是否使用安全通道; MockHttpServletRequestBuilder secure(boolean secure) //:请求的后处理器,用于自定义一些请求处理的扩展点; MockHttpServletRequestBuilder with(RequestPostProcessor postProcessor)
//:指定要上传的文件;
MockMultipartHttpServletRequestBuilder file(String name, byte[] content)/MockMultipartHttpServletRequestBuilder file(MockMultipartFile file)
//:添加验证断言来判断执行请求后的结果是否是预期的;
ResultActions andExpect(ResultMatcher matcher)
//:添加结果处理器,用于对验证成功后执行的动作,如输出下请求/结果信息用于调试;
ResultActions andDo(ResultHandler handler)
//:返回验证成功后的MvcResult;用于自定义验证/下一步的异步处理;
MvcResult andReturn()
//:请求的Handler验证器,比如验证处理器类型/方法名;此处的Handler其实就是处理请求的控制器; HandlerResultMatchers handler() //:得到RequestResultMatchers验证器; RequestResultMatchers request() //:得到模型验证器; ModelResultMatchers model() //:得到视图验证器; ViewResultMatchers view() //:得到Flash属性验证; FlashAttributeResultMatchers flash() //:得到响应状态验证器; StatusResultMatchers status() //:得到响应Header验证器; HeaderResultMatchers header() //:得到响应Cookie验证器; CookieResultMatchers cookie() //:得到响应内容验证器; ContentResultMatchers content() //:得到Json表达式验证器; JsonPathResultMatchers jsonPath(String expression, Object ... args)/ResultMatcher jsonPath(String expression, Matcher matcher) //:得到Xpath表达式验证器; XpathResultMatchers xpath(String expression, Object... args)/XpathResultMatchers xpath(String expression, Map<string, string=""> namespaces, Object... args) //:验证处理完请求后转发的url(绝对匹配); ResultMatcher forwardedUrl(final String expectedUrl) //:验证处理完请求后转发的url(Ant风格模式匹配,@since spring4); ResultMatcher forwardedUrlPattern(final String urlPattern) //:验证处理完请求后重定向的url(绝对匹配); ResultMatcher redirectedUrl(final String expectedUrl) //:验证处理完请求后重定向的url(Ant风格模式匹配,@since spring4); ResultMatcher redirectedUrlPattern(final String expectedUrl)
package com.firewolf.busi.example.springtest; 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.boot.test.mock.mockito.SpyBean; import org.springframework.http.MediaType; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; import org.springframework.test.web.servlet.ResultHandler; import org.springframework.test.web.servlet.result.MockMvcResultHandlers; import static org.mockito.Mockito.*; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; import static org.junit.jupiter.api.Assertions.*; @SpringBootTest @AutoConfigureMockMvc class UserControllerTest { @Autowired private MockMvc mockMvc; @MockBean private UserService userService; @Test void addUser() throws Exception { doNothing().when(userService).addUser(any(User.class)); MvcResult mockResult = mockMvc .perform(post("/users").contentType(MediaType.APPLICATION_FORM_URLENCODED) .param("account","liuxing").param("password","123345")) //传入参数 .andExpect(status().isOk()) // 断言状态 .andReturn(); //返回结果 assertEquals("success",mockResult.getResponse().getContentAsString()); //断言结果 } }
我们可以使用jsonpath来解析结果并进行断言,关于jsonpath的使用,可以来这里:【JsonPath官网】
示例:
package com.firewolf.busi.example.springtest; import org.hamcrest.MatcherAssert; 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.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.boot.test.mock.mockito.SpyBean; import org.springframework.http.MediaType; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; import org.springframework.test.web.servlet.result.MockMvcResultHandlers; import java.util.Arrays; import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.*; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @SpringBootTest @AutoConfigureMockMvc class UserControllerTest { @Autowired private MockMvc mockMvc; @MockBean private UserService userService; @Test void testSelectUser() throws Exception { List<User> users = Arrays.asList( User.builder().account("lx").build(), User.builder().account("liuxing").build() ); doReturn(users).when(userService).selectUser(); //使用JsonPath解析结果并进行断言 mockMvc.perform(get("/users")) .andExpect(jsonPath("$.size()").value(2)) .andExpect(jsonPath("$..account").isArray()) .andExpect(jsonPath("$.[0].account").value("lx")) .andDo(MockMvcResultHandlers.print(System.out)) .andDo(mvcResult -> assertTrue(mvcResult.getResponse().getContentAsString().contains("liuxing"))); } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。