MockMVC的使用
Posted LonZyuan
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MockMVC的使用相关的知识,希望对你有一定的参考价值。
简介
MockMvc是一种基于Java和JUnit的测试框架,旨在测试Spring MVC应用程序的控制器层。它可以帮助我们模拟HTTP请求,检查响应值,以及发送表单数据和文件等。 MockMvc可以与其他测试组件(如JUnit和Hamcrest)结合使用,以模拟REST API端点或用户界面的操作。
Get请求
简单示例
创建一个简单的Controller:
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/mockMcv") public class MockMvcController @GetMapping("/first/connect") public String testConnect() String result = "Hello MockMVC"; return result;
创建测试类,使用MockMVC
import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; 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.test.context.junit4.SpringRunner; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.result.MockMvcResultMatchers; // webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT: // 指示Spring Boot在随机且可用的端口上启动Web服务器,避免端口冲突 @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) // 自动配置MockMvc和Spring IOC容器 @AutoConfigureMockMvc @RunWith(SpringRunner.class) public class MockMvcControllerTest @Autowired private MockMvc mockMvc; @Test public void testConnect() throws Exception MvcResult mvcResult = mockMvc.perform( // Get请求,url为:/mockMcv/first/connect MockMvcRequestBuilders.get("/mockMcv/first/connect")) // 检查响应状态码是否为200 .andExpect(MockMvcResultMatchers.status().isOk()) // 返回一个MvcResult回调对象 .andReturn(); // 获取响应文本 String content = mvcResult.getResponse().getContentAsString(); // 通过断言来验证返回是否正确 Assert.assertTrue(content.equals("Hello MockMVC"));
直接启动Debug启动,请求成功:
注意点
上述示例中需要注意,@SpringBootTest注解需要括号中的内容
去掉@SpringBootTest括号中的内容,运行结果:
原因:@SpringBootTest注释用于在测试与Spring Boot应用程序的集成中启用Spring上下文,如果使用默认配置,那就是Mock模式,即加载一个伪造的Web环境,其中一些内容(例如HTTP会话和重定向)不实际触发,而只是返回一个默认值。
所以在测试期间尝试模拟HTTP请求并使用不实际存在的Session时,可能会从该会话对象中出现NullPointerException。
Get带参数
controller中新加一个带参数的Get请求:
@GetMapping("/connect/withParam") public String testConnectWithParam(@RequestParam("id") Long id, @RequestParam("name") String name) String result = "id:" + id + ",name:" + name; return result;
测试类新增对应的测试方法:
@Test public void testConnectWithParam() throws Exception MvcResult mvcResult = mockMvc.perform( MockMvcRequestBuilders.get("/mockMcv/connect/withParam") // 通过param()存放各个请求参数 .param("id", "1") .param("name", "MockMVC")) .andExpect(MockMvcResultMatchers.status().isOk()) .andReturn(); String content = mvcResult.getResponse().getContentAsString(); Assert.assertTrue(content.contains("MockMVC"));
Debug启动,请求成功:
Post请求
示例
controller中新加一个带Body的Post请求:
@PostMapping("/connect/withBody") public String testConnectWithBody(@RequestBody Map<String,String> requestBody) String result = JSONArray.toJSONString(requestBody); return result;
新增一个Java对象存放值:
import lombok.Data; import java.io.Serializable; @Data public class MockMVCInfo implements Serializable private static final Long serializableID = -1L; private Long id; private String name;
测试类新增对应的测试方法:
@Test public void testConnectWithBody() throws Exception MockMVCInfo mockMVCInfo = new MockMVCInfo(); mockMVCInfo.setId(3L); mockMVCInfo.setName("MockMVC"); MvcResult mvcResult = mockMvc.perform( MockMvcRequestBuilders.post("/mockMcv/connect/withBody") // 指定 header中的 Content-Type为 application/json .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) // 通过content()存放Body的Json .content(JSON.toJSONString(mockMVCInfo))) .andExpect(MockMvcResultMatchers.status().isOk()) .andReturn(); String content = mvcResult.getResponse().getContentAsString(); Assert.assertTrue(content.contains("MockMVC"));
Debug启动,请求成功:
注意点
1.通过RequestBody传递值,就得使用.content()方法存放Json
2.Body中使用Json,需要在header中指定Content-Type为application/json,参考Postman:
MockMVC - 如何使用 org.hamcrest.Matcher 在春季安全集成测试中检查 JWT 令牌的内容
【中文标题】MockMVC - 如何使用 org.hamcrest.Matcher 在春季安全集成测试中检查 JWT 令牌的内容【英文标题】:MockMVC - How to check the content of a JWT token in a spring security integration test with org.hamcrest.Matcher 【发布时间】:2019-05-04 20:50:08 【问题描述】:我会从 MockMvc 请求中获取 JWT 令牌作为响应。我想检查一下这个回复的内容:
mockMvc.perform(post("/authorize")
.header(HttpHeaders.AUTHORIZATION, "Basic " + encodeEmailAndPassword("test1@app.com", "1111"))
.contentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE)
.params(params)
.accept(MediaType.APPLICATION_JSON))
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8))
.andDo(print())
.andExpect(status().isOk())
;
结果将是:
"id_token": "............(long Base64 string)"
当我们使用 JWT.io 解码令牌时,我们会看到:
"sub": "cc15a160-2d62-4091-b89a-117e77346a58",
"nbf": 1543846725,
"auth_level": "trusted",
"iss": "http://localhost:9090/",
"exp": 1543847724,
"iat": 1543846725,
"nonce": "random_string",
"jti": "64b8b6e3-5cd0-4242-bcea-2c5d498d64c1"
一切都很好,但我想做这样的事情:
.andExpect(jsonPath("$.id_token", Matchers.not(null)))
.andExpect(decodeJWT(jsonPath("$.id_token")).getValueOf("nonce"), Matchers.is("random_string"));
我该怎么做?
【问题讨论】:
【参考方案1】:好吧,我自己找到了答案...基本上,org.hamcrest.Matcher
是不可能的,但我们可以将响应分成几部分并将它们映射到 DTO。
首先,我做一些状态和基本检查,然后将响应返回为MvcResult
:
MvcResult result = mockMvc.perform(post("/authorize")
.header(HttpHeaders.AUTHORIZATION, "Basic " + encodeEmailAndPassword("test1@app.com", "1111"))
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(content)
.accept(MediaType.APPLICATION_JSON))
.andDo(print())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8))
.andExpect(status().isOk())
.andExpect(jsonPath("$.id_token", Matchers.notNullValue()))
.andReturn();
然后,我为 Jackson 反序列化创建了一些 DTO:(记得创建类 not 作为内部类,因为 Jackson 会抱怨“只能使用内部非静态类的无参数构造函数")
@Data
@NoArgsConstructor
@AllArgsConstructor
@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class)
class TokenResponseDTO implements Serializable
//@JsonProperty("id_token")
private String idToken;
@Data
@NoArgsConstructor
@AllArgsConstructor
@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class)
class JWTPayloadDTO implements Serializable
private String aud;
private String sub;
private String nbf;
private String authLevel;
private String iss;
private Long exp;
private Long iat;
private String nonce;
private String jti;
终于,JWT 令牌解析比我想象的要容易得多:
String token = mapper.readValue(result.getResponse().getContentAsString(), TokenResponseDTO.class).getIdToken();
JWSObject jwsObject = JWSObject.parse(token);
JWTPayloadDTO payload = mapper.readValue(jwsObject.getPayload().toString(), JWTPayloadDTO.class);
Assert.assertEquals("random_string", payload.getNonce());
... // other checks
【讨论】:
谢谢。你的回答对我很有帮助。以上是关于MockMVC的使用的主要内容,如果未能解决你的问题,请参考以下文章
MockMVC - 如何使用 org.hamcrest.Matcher 在春季安全集成测试中检查 JWT 令牌的内容
使用 MockMvc 测试重定向 URL 的 HTTP 状态码
在 Spring Boot 1.4 MVC 测试中使用 @WebMvcTest 设置 MockMvc