1 网上的案例
SpringBoot创建的Maven项目中,会默认添加spring-boot-starter-test依赖。在《5分钟快速上手SpringBoot》中编写的单元测试使用了MockMvc。
什么是Mock
在面向对象的程序设计中,模拟对象(英语:mock object)是以可控的方式模拟真实对象行为的假对象。在编程过程中,通常通过模拟一些输入数据,来验证程序是否达到预期结果。
对于前后端分离的项目而言,无法直接从前端静态代码中测试接口的正确性,因此可以通过MockMVC来模拟HTTP请求。基于RESTful风格的SpringMVC的测试,我们可以测试完整的Spring MVC流程,即从URL请求到控制器处理,再到视图渲染都可以测试。
SpringBoot中使用
第一步:jar包引入。创建SpringBoot项目中默认引入的spring-boot-starter-test间接引入了spring-test,因此无需再额外引入jar包。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
第二步:创建Controller类,并提供方法作为待测试的业务接口。
@RestController
public class Controller {
@RequestMapping
public String hello(String name){
return "Hello " + name + "!";
}
}
第三步:编写测试类。实例化MockMvc有两种形式,一种是使用StandaloneMockMvcBuilder,另外一种是使用DefaultMockMvcBuilder。测试类及初始化MockMvc初始化:
//SpringBoot1.4版本之前用的是SpringJUnit4ClassRunner.class
@RunWith(SpringRunner.class)
//SpringBoot1.4版本之前用的是@SpringApplicationConfiguration(classes = Application.class)
@SpringBootTest
//测试环境使用,用来表示测试环境使用的ApplicationContext将是WebApplicationContext类型的
@WebAppConfiguration
public class HelloWorldTest {
private MockMvc mockMvc;
@Autowired
private WebApplicationContext webApplicationContext;
@Before
public void setup() {
// 实例化方式一
// mockMvc = MockMvcBuilders.standaloneSetup(new HelloWorldController()).build();
// 实例化方式二
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
@Test
public void testHello() throws Exception {
/*
* 1、mockMvc.perform执行一个请求。
* 2、MockMvcRequestBuilders.get("XXX")构造一个请求。
* 3、ResultActions.param添加请求传值
* 4、ResultActions.accept(MediaType.TEXT_HTML_VALUE))设置返回类型
* 5、ResultActions.andExpect添加执行完成后的断言。
* 6、ResultActions.andDo添加一个结果处理器,表示要对结果做点什么事情
* 比如此处使用MockMvcResultHandlers.print()输出整个响应结果信息。
* 7、ResultActions.andReturn表示执行完成后返回相应的结果。
*/
mockMvc.perform(MockMvcRequestBuilders
.get("/hello")
// 设置返回值类型为utf-8,否则默认为ISO-8859-1
.accept(MediaType.APPLICATION_JSON_UTF8_VALUE)
.param("name", "Tom"))
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.content().string("Hello Tom!"))
.andDo(MockMvcResultHandlers.print());
//.andReturn();//想要返回结果,使用此方法
}
测试结果打印:
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 200
Error message = null
Headers = [Content-Type:"application/json;charset=UTF-8", Content-Length:"10"]
Content type = application/json;charset=UTF-8
Body = Hello Tom!
Forwarded URL = null
Redirected URL = null
Cookies = []
2019-04-02 21:34:27.954 INFO 6937 --- [ Thread-2] o.s.s.concurrent.ThreadPoolTaskExecutor : Shutting down ExecutorService 'applicationTaskExecutor'
整个过程如下:
1、准备测试环境
2、通过MockMvc执行请求
3、添加验证断言
4、添加结果处理器
5、得到MvcResult进行自定义断言/进行下一步的异步请求
6、卸载测试环境
参考:https://cloud.tencent.com/developer/article/1435504
以上是常规用法,网上一大堆。
2 我自己怎么用的?遇到的问题以及解决
背景就是用的shiro写的登录权限验证,然后开始测试的时候,就各种报错。
错误一:
java.lang.IllegalStateException: Unable to find a @SpringBootConfiguration, you need to use…
解决:没有启动类;虽然你写了启动类但是你的启动类所在的包和单元测试的包不在同一级根目录下。(改了测试类的包名字)放在同一个包目录下就解决这个问题。
参考:https://blog.csdn.net/wudajushi/article/details/77435528
错误二:
org.apache.shiro.UnavailableSecurityManagerException: No SecurityManager accessible to the calling code, either bound to the org.apache.shiro.util.ThreadContextor as a vm static singleton. This is an invalid application configuration.
解决:就是测试之前增加请求验证。代码如下:
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.ThreadContext;
import org.apache.shiro.web.subject.WebSubject;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.mock.web.MockHttpSession;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import org.apache.shiro.mgt.SecurityManager;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@SpringBootTest
@WebAppConfiguration
@RunWith(SpringRunner.class)
@ComponentScan
public class AuthApplicationTests {
private MockMvc mockMvc;
@Autowired
private WebApplicationContext webApplicationContext;
@Autowired
private SecurityManager securityManager;
@Before
public void contextLoads() {
MockHttpServletRequest mockHttpServletRequest = new MockHttpServletRequest(webApplicationContext.getServletContext());
MockHttpServletResponse mockHttpServletResponse = new MockHttpServletResponse();
MockHttpSession mockHttpSession = new MockHttpSession(webApplicationContext.getServletContext());
mockHttpServletRequest.setSession(mockHttpSession);
SecurityUtils.setSecurityManager(securityManager);
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
Subject subject = new WebSubject
.Builder(mockHttpServletRequest, mockHttpServletResponse)
.buildWebSubject();
UsernamePasswordToken token = new UsernamePasswordToken("super", "123");
subject.login(token);
ThreadContext.bind(subject);
}
@Test
public void testLogin() throws Exception {
mockMvc.perform(MockMvcRequestBuilders
.post("/auth/user/login")
// 设置返回值类型为utf-8,否则默认为ISO-8859-1
.accept(MediaType.APPLICATION_JSON_UTF8_VALUE)
.param("loginName", "lixixi")
.param("password", "123")
)
.andExpect(status().isOk())
.andDo(print()); //打印结果
}
@Test
public void testAddUser() throws Exception {
//接口中接收的参数为对象的话,可以进行(Json对象或者实体类的)封装
JSONObject param = new JSONObject() ;
param.put("loginName", "111");
param.put("password", "111");
param.put("userType","USER");
String jsonstr = param.toString() ;
System.out.println("================================请求入参:"+jsonstr);
mockMvc.perform(MockMvcRequestBuilders
.post("/auth/user/info")
.accept(MediaType.APPLICATION_JSON_UTF8_VALUE)
.param("token", "976e02095de04c5xxxxxxxxxc7b2ba")
.contentType(MediaType.APPLICATION_JSON)
.content(jsonstr).accept(MediaType.APPLICATION_JSON)
)
.andExpect(status().isOk())
.andDo(print()); //打印结果
}
}