Java 如何测试JSON路径是否不包含特定元素,或者该元素是否存在,是否为null?

Java 如何测试JSON路径是否不包含特定元素,或者该元素是否存在,是否为null?,java,unit-testing,spring-mvc,jsonpath,mockmvc,Java,Unit Testing,Spring Mvc,Jsonpath,Mockmvc,我一直在为一个简单的SpringWeb应用程序编写一些简单的单元测试例程。在资源的getter方法上添加@JsonIgnore注释时,生成的json对象不包含相应的json元素。因此,当我的单元测试例程尝试测试这是否为null(这是我的案例的预期行为,我不希望密码在json对象中可用)时,测试例程会遇到异常: java.lang.AssertionError:JSON路径没有值:$。密码,异常:路径没有结果:$['password'] 这是我编写的单元测试方法,使用is(nullValue())

我一直在为一个简单的SpringWeb应用程序编写一些简单的单元测试例程。在资源的getter方法上添加@JsonIgnore注释时,生成的json对象不包含相应的json元素。因此,当我的单元测试例程尝试测试这是否为null(这是我的案例的预期行为,我不希望密码在json对象中可用)时,测试例程会遇到异常:

java.lang.AssertionError:JSON路径没有值:$。密码,异常:路径没有结果:$['password']

这是我编写的单元测试方法,使用is(nullValue())方法测试“password”字段:

我还尝试了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);
    }
}
我理解为什么会出现异常,在某种程度上,测试是成功的,因为它找不到密码字段。但我想做的是,运行此测试以确保字段不存在,或者如果存在,则它包含null值。我怎样才能做到这一点

堆栈溢出中也有类似的post:

就我而言,这个领域可能也不存在

作为记录,以下是我正在使用的测试包的版本:

    <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>

@JsonIgnore的行为与预期的一样,没有在json输出中生成密码,那么您怎么能期望测试从输出中显式排除的东西呢

该行:

.andExpect(jsonPath("$.property", is("some value")));
甚至测试属性是否为空:

.andExpect(jsonPath("$.property").value(IsNull.nullValue()));
对应类似于json的:

{
...
"property": "some value",
...
}
如果重要部分是左侧,即“财产”的存在:

相反,@JsonIgnore根本没有在输出中生成porperty,因此您不能期望它不在测试中,也不能在生产输出中。 如果您不想在输出中使用该属性,这是可以的,但您不能期望在测试中使用它。 如果您希望它在输出中空空如也(无论是在PROD和测试中),都要在中间创建一个静态映射器方法,该方法不将属性的值传递给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);
    }
}
@RequestMapping(value=“/users/{userId}”,method=RequestMethod.GET)
公共响应属性getUser(@PathVariable Long userId){
info(“对参数为{},userId的getUser()的请求已到达);
User User=Mapper.mappernotrest(userService.getUserById(userId));
如果(用户!=null){
UserResource UserResource=新的UserResourceAsm().toResource(用户);
返回新的响应属性(userResource,HttpStatus.OK);
}否则{
返回新的ResponseEntity(未找到HttpStatus.NOT_);
}
}
此时,如果您希望Mapper.mapPersonToRest返回一个密码为空的用户,那么您可以在此方法上编写一个普通的单元测试


当然,密码是在数据库中加密的,对吗?;)

我对新版本也有同样的问题。在我看来,doesNotExist()函数将验证键是否不在结果中:

.andExpect(jsonPath("$.password").doesNotExist())

doethavejsonpath
为了检查它是否不在json正文中

我想重用用于测试提供的参数和缺少的参数的相同代码,这就是我想到的

  @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
值的属性与根本不存在的属性之间存在差异

如果测试仅在存在非空值时失败,请使用:

.andExpect(jsonPath("password").doesNotExist())
.andExpect(jsonPath("password").doesNotHaveJsonPath())
如果属性一出现测试就失败,即使使用
null
值,也应使用:

.andExpect(jsonPath("password").doesNotExist())
.andExpect(jsonPath("password").doesNotHaveJsonPath())

是的,JsonIgnore正在按预期运行。那么,在某些字段上使用JsonIgnore测试实体的最佳实践是什么呢。假设这样,假设您想要编写一个测试,您知道哪些字段应该为空或null(或者甚至不应该存在),但是您不会打开源代码并读取它们上是否有JsonIgnore注释。很明显,你希望你的测试通过,而不是异常失败。哦,当然密码是散列的。这并不重要,只是一个测试项目。您要做的是确保object UserResource没有返回密码,对吗?假设您有一个新的UserResourceAsm().toResource(user)返回用户,对吗?在这种情况下,您不应该在SpringMVC级别进行测试,而应该执行一个普通的单元测试,检查user.getPassword()是否为null。希望这能澄清!我加了一个例子来帮助你理解我的意思。让我看看你有没有更多的怀疑!我问这个问题的原因是因为我在遵循的教程中看到了类似的东西。但教程作者使用了hamcrest/mockito的旧版本。这可能是他获得测试成功的原因吗?我已经测试过了,我认为我的版本是正确的。使用json path和json path assert版本0.9.1,我的测试通过。即使字段不存在,null测试也会成功。然而,对于较新的版本,我认为您在回答中描述的关于Mapper的使用是首选方法。我只是在学习单元测试的诀窍。我就是这么做的,看看我问题的最后一行。[问题最后一次编辑于2015年9月4日14:55]如果您使用AssertJ(例如,在Spring Boot应用程序中),这是检查它的方式
assertThat(this.json.write(entity)).doesNotHaveJsonPathValue(@.keyl”)
此外,要检查json中是否不存在属性(换句话说,json是一个空对象:
{}
),您可以使用
.andExpect(jsonPath($*).doesNotExist())
,感谢您上次的“编辑”。
.doesNotExist()
就是我要找的。
.andExpect(jsonPath("password").doesNotExist())
.andExpect(jsonPath("password").doesNotHaveJsonPath())