如何测试 JSON 路径是否不包含特定元素,或者该元素是否存在且为空?
How to test if JSON path does not include a specific element, or if the element is present it is null?
我一直在为一个简单的 spring Web 应用程序编写一些简单的单元测试例程。当我在资源的 getter 方法上添加 @JsonIgnore 注释时,生成的 json 对象不包含相应的 json 元素。因此,当我的单元测试例程尝试测试这是否为空时(这是我的情况的预期行为,我不希望密码在 json 对象中可用),测试例程 运行s进入异常:
java.lang.AssertionError: No value for JSON path: $.password, exception: No results for path: $['password']
这是我写的单元测试方法,用is(nullValue())方法测试'password'字段:
@Test
public void getUserThatExists() throws Exception {
User user = new User();
user.setId(1L);
user.setUsername("zobayer");
user.setPassword("123456");
when(userService.getUserById(1L)).thenReturn(user);
mockMvc.perform(get("/users/1"))
.andExpect(jsonPath("$.username", is(user.getUsername())))
.andExpect(jsonPath("$.password", is(nullValue())))
.andExpect(jsonPath("$.links[*].href", hasItem(endsWith("/users/1"))))
.andExpect(status().isOk())
.andDo(print());
}
我也尝试过使用 jsonPath().exists() 得到类似的异常,说明路径不存在。我正在分享更多代码片段,以便整个情况变得更具可读性。
我正在测试的控制器方法看起来像这样:
@RequestMapping(value="/users/{userId}", method= RequestMethod.GET)
public ResponseEntity<UserResource> getUser(@PathVariable Long userId) {
logger.info("Request arrived for getUser() with params {}", userId);
User user = userService.getUserById(userId);
if(user != null) {
UserResource userResource = new UserResourceAsm().toResource(user);
return new ResponseEntity<>(userResource, HttpStatus.OK);
} else {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
}
我正在使用 spring hateos 资源汇编程序将实体转换为资源对象,这是我的资源 class:
public class UserResource extends ResourceSupport {
private Long userId;
private String username;
private String password;
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
@JsonIgnore
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
我明白为什么会出现异常,同样在某种程度上,测试成功,找不到密码字段。但我想做的是,运行 这个测试确保该字段不存在,或者如果存在,它包含空值。我怎样才能做到这一点?
stack overflow也有类似的post:
Hamcrest with MockMvc: check that key exists but value may be null
在我的例子中,该字段可能也不存在。
作为记录,这些是我使用的测试包的版本:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.6.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.6.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.6.1</version>
</dependency>
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<version>2.0.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path-assert</artifactId>
<version>2.0.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.10.19</version>
<scope>test</scope>
</dependency>
提前致谢。
[编辑]
更准确地说,你必须为一个实体编写测试,你知道其中一些字段需要为 null 或空,或者甚至不应该存在,而你实际上并没有通过代码来查看是否存在在 属性 之上添加了一个 JsonIgnore。而你希望你的测试通过,我该怎么做。
请随时告诉我这根本不实用,但仍然很高兴知道。
[编辑]
上面的测试通过以下较旧的 json-path 依赖项成功:
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<version>0.9.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path-assert</artifactId>
<version>0.9.1</version>
<scope>test</scope>
</dependency>
[编辑] 在阅读 spring 的 json 路径匹配器的文档后,找到了适用于最新版本 jayway.jasonpath 的快速修复。
.andExpect(jsonPath("$.password").doesNotExist())
@JsonIgnore 的行为符合预期,没有在 json 输出中生成密码,那么您怎么能期望测试您明确从输出中排除的内容呢?
行:
.andExpect(jsonPath("$.property", is("some value")));
甚至 属性 为空的测试:
.andExpect(jsonPath("$.property").value(IsNull.nullValue()));
对应一个json像:
{
...
"property": "some value",
...
}
其中重要的部分是左侧,即"property"的存在:
相反,@JsonIgnore 根本不会在输出中生成该属性,因此您不能指望它既不会出现在测试中,也不会出现在生产输出中。
如果你不想要输出中的 属性 ,那很好,但你不能在测试中期望它。
如果你希望它在输出中为空(在产品和测试中)你想在中间创建一个静态映射器方法,它不会将 属性 的值传递给 json 对象:
Mapper.mapPersonToRest(User user) {//exclude the password}
然后你的方法是:
@RequestMapping(value="/users/{userId}", method= RequestMethod.GET)
public ResponseEntity<UserResource> getUser(@PathVariable Long userId) {
logger.info("Request arrived for getUser() with params {}", userId);
User user = Mapper.mapPersonToRest(userService.getUserById(userId));
if(user != null) {
UserResource userResource = new UserResourceAsm().toResource(user);
return new ResponseEntity<>(userResource, HttpStatus.OK);
} else {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
}
此时,如果您的期望是 Mapper.mapPersonToRest 到 return 具有空密码的用户,您可以在此方法上编写一个正常的单元测试。
P.S。当然密码在数据库上是加密的,对吧? ;)
我在使用较新版本时遇到了同样的问题。在我看来,doesNotExist() 函数将验证密钥不在结果中:
.andExpect(jsonPath("$.password").doesNotExist())
doesNotHaveJsonPath
用于检查它不在 json body
中
我想重复使用相同的代码来测试所提供的参数,以及它是否丢失,这就是我想出的
@Test
void testEditionFoundInRequest() throws JsonProcessingException {
testEditionWithValue("myEdition");
}
@Test
void testEditionNotFoundInRequest() {
try {
testEditionWithValue(null);
throw new RuntimeException("Shouldn't pass");
} catch (AssertionError | JsonProcessingException e) {
var msg = e.getMessage();
assertTrue(msg.contains("No value at JSON path"));
}
}
void testEditionWithValue(String edition) {
var HOST ="fakeHost";
var restTemplate = new RestTemplate();
var myRestClientUsingRestTemplate = new MyRestClientUsingRestTemplate(HOST, restTemplate);
MockRestServiceServer mockServer;
ObjectMapper objectMapper = new ObjectMapper();
String id = "userId";
var mockResponse = "{}";
var request = new MyRequest.Builder(id).edition(null).build();
mockServer = MockRestServiceServer.bindTo(restTemplate).bufferContent().build();
mockServer
.expect(method(POST))
// THIS IS THE LINE I'd like to say "NOT" found
.andExpect(jsonPath("$.edition").value(edition))
.andRespond(withSuccess(mockResponse, APPLICATION_JSON));
var response = myRestClientUsingRestTemplate.makeRestCall(request);
} catch (AssertionError | JsonProcessingException e) {
var msg = e.getMessage();
assertTrue(msg.contains("No value at JSON path"));
}
存在但具有 null
值的 属性 与根本不存在的 属性 之间存在差异。
如果测试失败 只有 当存在非 null
值时,使用:
.andExpect(jsonPath("password").doesNotExist())
如果 属性 出现时测试失败,即使有 null
值,使用:
.andExpect(jsonPath("password").doesNotHaveJsonPath())
我一直在为一个简单的 spring Web 应用程序编写一些简单的单元测试例程。当我在资源的 getter 方法上添加 @JsonIgnore 注释时,生成的 json 对象不包含相应的 json 元素。因此,当我的单元测试例程尝试测试这是否为空时(这是我的情况的预期行为,我不希望密码在 json 对象中可用),测试例程 运行s进入异常:
java.lang.AssertionError: No value for JSON path: $.password, exception: No results for path: $['password']
这是我写的单元测试方法,用is(nullValue())方法测试'password'字段:
@Test
public void getUserThatExists() throws Exception {
User user = new User();
user.setId(1L);
user.setUsername("zobayer");
user.setPassword("123456");
when(userService.getUserById(1L)).thenReturn(user);
mockMvc.perform(get("/users/1"))
.andExpect(jsonPath("$.username", is(user.getUsername())))
.andExpect(jsonPath("$.password", is(nullValue())))
.andExpect(jsonPath("$.links[*].href", hasItem(endsWith("/users/1"))))
.andExpect(status().isOk())
.andDo(print());
}
我也尝试过使用 jsonPath().exists() 得到类似的异常,说明路径不存在。我正在分享更多代码片段,以便整个情况变得更具可读性。
我正在测试的控制器方法看起来像这样:
@RequestMapping(value="/users/{userId}", method= RequestMethod.GET)
public ResponseEntity<UserResource> getUser(@PathVariable Long userId) {
logger.info("Request arrived for getUser() with params {}", userId);
User user = userService.getUserById(userId);
if(user != null) {
UserResource userResource = new UserResourceAsm().toResource(user);
return new ResponseEntity<>(userResource, HttpStatus.OK);
} else {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
}
我正在使用 spring hateos 资源汇编程序将实体转换为资源对象,这是我的资源 class:
public class UserResource extends ResourceSupport {
private Long userId;
private String username;
private String password;
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
@JsonIgnore
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
我明白为什么会出现异常,同样在某种程度上,测试成功,找不到密码字段。但我想做的是,运行 这个测试确保该字段不存在,或者如果存在,它包含空值。我怎样才能做到这一点?
stack overflow也有类似的post: Hamcrest with MockMvc: check that key exists but value may be null
在我的例子中,该字段可能也不存在。
作为记录,这些是我使用的测试包的版本:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.6.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.6.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.6.1</version>
</dependency>
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<version>2.0.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path-assert</artifactId>
<version>2.0.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.10.19</version>
<scope>test</scope>
</dependency>
提前致谢。
[编辑] 更准确地说,你必须为一个实体编写测试,你知道其中一些字段需要为 null 或空,或者甚至不应该存在,而你实际上并没有通过代码来查看是否存在在 属性 之上添加了一个 JsonIgnore。而你希望你的测试通过,我该怎么做。
请随时告诉我这根本不实用,但仍然很高兴知道。
[编辑] 上面的测试通过以下较旧的 json-path 依赖项成功:
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<version>0.9.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path-assert</artifactId>
<version>0.9.1</version>
<scope>test</scope>
</dependency>
[编辑] 在阅读 spring 的 json 路径匹配器的文档后,找到了适用于最新版本 jayway.jasonpath 的快速修复。
.andExpect(jsonPath("$.password").doesNotExist())
@JsonIgnore 的行为符合预期,没有在 json 输出中生成密码,那么您怎么能期望测试您明确从输出中排除的内容呢?
行:
.andExpect(jsonPath("$.property", is("some value")));
甚至 属性 为空的测试:
.andExpect(jsonPath("$.property").value(IsNull.nullValue()));
对应一个json像:
{
...
"property": "some value",
...
}
其中重要的部分是左侧,即"property"的存在:
相反,@JsonIgnore 根本不会在输出中生成该属性,因此您不能指望它既不会出现在测试中,也不会出现在生产输出中。 如果你不想要输出中的 属性 ,那很好,但你不能在测试中期望它。 如果你希望它在输出中为空(在产品和测试中)你想在中间创建一个静态映射器方法,它不会将 属性 的值传递给 json 对象:
Mapper.mapPersonToRest(User user) {//exclude the password}
然后你的方法是:
@RequestMapping(value="/users/{userId}", method= RequestMethod.GET)
public ResponseEntity<UserResource> getUser(@PathVariable Long userId) {
logger.info("Request arrived for getUser() with params {}", userId);
User user = Mapper.mapPersonToRest(userService.getUserById(userId));
if(user != null) {
UserResource userResource = new UserResourceAsm().toResource(user);
return new ResponseEntity<>(userResource, HttpStatus.OK);
} else {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
}
此时,如果您的期望是 Mapper.mapPersonToRest 到 return 具有空密码的用户,您可以在此方法上编写一个正常的单元测试。
P.S。当然密码在数据库上是加密的,对吧? ;)
我在使用较新版本时遇到了同样的问题。在我看来,doesNotExist() 函数将验证密钥不在结果中:
.andExpect(jsonPath("$.password").doesNotExist())
doesNotHaveJsonPath
用于检查它不在 json body
我想重复使用相同的代码来测试所提供的参数,以及它是否丢失,这就是我想出的
@Test
void testEditionFoundInRequest() throws JsonProcessingException {
testEditionWithValue("myEdition");
}
@Test
void testEditionNotFoundInRequest() {
try {
testEditionWithValue(null);
throw new RuntimeException("Shouldn't pass");
} catch (AssertionError | JsonProcessingException e) {
var msg = e.getMessage();
assertTrue(msg.contains("No value at JSON path"));
}
}
void testEditionWithValue(String edition) {
var HOST ="fakeHost";
var restTemplate = new RestTemplate();
var myRestClientUsingRestTemplate = new MyRestClientUsingRestTemplate(HOST, restTemplate);
MockRestServiceServer mockServer;
ObjectMapper objectMapper = new ObjectMapper();
String id = "userId";
var mockResponse = "{}";
var request = new MyRequest.Builder(id).edition(null).build();
mockServer = MockRestServiceServer.bindTo(restTemplate).bufferContent().build();
mockServer
.expect(method(POST))
// THIS IS THE LINE I'd like to say "NOT" found
.andExpect(jsonPath("$.edition").value(edition))
.andRespond(withSuccess(mockResponse, APPLICATION_JSON));
var response = myRestClientUsingRestTemplate.makeRestCall(request);
} catch (AssertionError | JsonProcessingException e) {
var msg = e.getMessage();
assertTrue(msg.contains("No value at JSON path"));
}
存在但具有 null
值的 属性 与根本不存在的 属性 之间存在差异。
如果测试失败 只有 当存在非 null
值时,使用:
.andExpect(jsonPath("password").doesNotExist())
如果 属性 出现时测试失败,即使有 null
值,使用:
.andExpect(jsonPath("password").doesNotHaveJsonPath())