如何在spring-boot中测试json结构



我找到这段代码是为了测试json字符串是否等于另一个字符串

@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class MockMvcExampleTests {
@Autowired
private MockMvc mvc;
@Test
public void exampleTest() throws Exception {
this.mvc.perform(get("/")).andExpect(status().isOk())
.andExpect(content().string("Hello World"));
}
}

你知道如何测试json结构吗?比如检查对象是否包含id、name、age。。。不管价值是多少。谢谢

验证正确JSON响应的最佳方法取决于您的响应。

例如,假设您有一个@Controller(或@RestController(,它访问@Repository(直接或间接通过@Service(,并将结果项映射到JSON。例如:

public class MyPojo {
private int id;
private String name;
private int age;
// no-args for deserialization, required by some Frameworks with default settings
public MyPojo() {}
// constructor
public MyPojo(final int id, final String name, final int age) { ... }

// Getters
public int getid() { ... }
public String getName() { ... }
public int getName() { ... }
}
@Repository
public class MyRepository {
public MyPojo findMyPojoById(final int id) { return db.findById(id); }
}
@RestController
@RequestMapping("/")
public class MyController {
@Autowired
private MyRepository repository;
@GetMapping
public MyPojo findMyPojoById(@RequestParam final int id) {
return repository.findMyPojoByid(id);
}
}

然后,带有?id=1GET请求可能会返回以下内容:

{
"id": 1,
"name": "John",
"age": 12
}

在这种情况下,您有多种选择。在mockMvc测试中,您通常对测试组件的集成不感兴趣,只希望测试控制器是否按预期工作。在这种情况下,您可能需要单元测试,并模拟存储库。你有一些选择:

1.通过模拟的预期反应

在这种情况下,您可以执行以下操作:

@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class MockMvcExampleTests {
@Autowired
private MockMvc mvc;

@MockBean
private MyRepository repository; // mock the repository

@Autowired
private ObjectMapper objectMapper;
@Test
public void exampleTest() throws Exception {
final int userId = 1;
MyPojo mockedUser = new MyPojo(userId, "John", 12);
Mockito.doReturn(mockedUser).when(repository).findMyPojoById(userId);

final String expectedResponseContent = objectMapper.writeValueAsString(mockedUser);

this.mvc.perform(get("/"))
.andExpect(status().isOk())
.andExpect(content().json(expectedResponseContent ));

verify(repository).findMyPojoById(userId); // verify that the repository was called correctly
}
}

这样做的好处是测试是稳健的。如果你的MyPojo对象发生了变化,只要你没有在测试中更新模拟对象的所有构造函数用法,你的测试就会失败——这是一个没有意义的问题,因为在你更新之前,代码不会编译。

2.通过jsonPath()

在这种情况下,可以使用jsonPath(一种计算JSON结构上的表达式的方法,类似于XMLXPath(单独计算JSON结构的部分或全部。

例如(仍然嘲笑响应(:

@Test
public void exampleTest() throws Exception {
final int userId = 1;
MyPojo mockedUser = new MyPojo(userId, "John", 12);
Mockito.doReturn(mockedUser).when(repository).findMyPojoById(userId); // note that this mock is not necessary, but it does make the test a unit test

this.mvc.perform(get("/"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.id", is(userId))) // is(<Object>) is e.g. a Hamcrest Matcher
.andExpect(jsonPath("$.name", is("John"))
.andExpect(jsonPath("$.age", is(12)))
verify(repository).findMyPojoById(userId);
}

请注意,如果您有一个响应对象数组,您可以使用适当的数组表示法来解析它们,例如:

.andExpect(jsonPath("$[0].name", is("Mary")))

这样做的好处是,您不必评估整个响应。例如,如果服务器响应包括服务器生成的数据,而这些数据在不消除太多实际代码逻辑的情况下很难复制或模拟(例如,通过模拟(,这可能会很有用。在这种情况下,您可以检查字段是否存在,而不考虑其实际值,例如:

.andExpect(jsonPath("$.serverGeneratedSecretKey", notNullValue()))

3.通过字符串匹配

最后,您可以比较实际响应:


String expectedResponse = "{"id": 1, "name":"John", "age": 12}";
String responseString = mockMvc.perform(...) 
.andExpect(status().isOk())
.andReturn()
.getResponse()
.getContentAsString();
assertEquals("Response does not match", expectedResponse, responseString);

然而,这种方式是非常多变的:

  • 您需要完全匹配JSON,这包括转义"和适当的白间距
  • 例如,ObjectMapper试图在JSON字段出现时遵守它们的顺序,但您永远不应该将代码的工作基于JSON对象的顺序(除了数组之外,JSON中的顺序没有定义(
  • 这种方法也使重构成为一场噩梦。添加一个字段会破坏测试,使您不得不更改预期的字符串——由于上述原因,很容易出错

对于这种方法,我想不出一个很好的用例,但是,好吧,如果你想要的话,它就在那里。

值得注意的是,Java13最终支持了文本块。这些块虽然可以更容易地编写预期的响应字符串,但对其他方面没有帮助。

最新更新