SpringBoot之mockMvc

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());  //打印结果
    }
}
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值