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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/32397690/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 12:28:27  来源:igfitidea点击:

How to test if JSON path does not include a specific element, or if the element is present it is null?

javaunit-testingspring-mvcjsonpathmockmvc

提问by Zobayer Hasan

I have been writing some simple unit testing routines for a simple spring web application. When I add @JsonIgnore annotation on a getter method of a resource, the resulting json object does not include the corresponding json element. So when my unit test routine tries to test if this is null (which is the expected behavior for my case, I don't want the password to be available in json object), test routine runs into an exception:

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

java.lang.AssertionError: No value for JSON path: $.password, exception: No results for path: $['password']

java.lang.AssertionError:JSON 路径无值:$.password,异常:路径无结果:$['password']

This is the unit test method I wrote, testing the 'password' field with is(nullValue()) method:

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

@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());
}

I have also tried it with jsonPath().exists() which gets similar exception stating that the path doesn't exist. I am sharing some more code snippets so that the whole situation becomes more readable.

我也用 jsonPath().exists() 尝试过它,它得到类似的异常,指出路径不存在。我正在分享一些更多的代码片段,以便整个情况变得更具可读性。

The controller method I am testing looks something like this:

我正在测试的控制器方法如下所示:

@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);
    }
}

I am using spring hateos resource assembler for converting entity to resource objects and this is my resource class:

我正在使用 spring hatos 资源汇编器将实体转换为资源对象,这是我的资源类:

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

I understand why this is getting an exception, also in a way, the test is successful that it could not find the password field. But what I want to do is, run this test to ensure that the field is not present, or if present, it contains null value. How can I achieve this?

我明白为什么这会出现异常,在某种程度上,测试成功了,因为它找不到密码字段。但我想要做的是,运行此测试以确保该字段不存在,或者如果存在,则它包含空值。我怎样才能做到这一点?

There is a similar post in stack overflow: Hamcrest with MockMvc: check that key exists but value may be null

堆栈溢出中有一个类似的帖子: Hamcrest with MockMvc: check the key exists but value may be null

In my case, the field may be non existent as well.

就我而言,该领域也可能不存在。

For the record, these are the versions of test packages I am using:

为了记录,这些是我正在使用的测试包的版本:

    <dependency>
        <groupId>com.fasterxml.Hymanson.core</groupId>
        <artifactId>Hymanson-core</artifactId>
        <version>2.6.1</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.Hymanson.core</groupId>
        <artifactId>Hymanson-annotations</artifactId>
        <version>2.6.1</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.Hymanson.core</groupId>
        <artifactId>Hymanson-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>

Thanks in advance.

提前致谢。

[EDIT] To be more precise, say, you have to write a test for an entity where you know some of the fields need to be null or empty or should not even exists, and you don't actually go through the code to see if there is a JsonIgnore added on top of the property. And you want your tests to pass, how can I do this.

[编辑] 更准确地说,您必须为一个实体编写一个测试,其中您知道某些字段需要为空或为空或什至不应该存在,并且您实际上并没有通过代码来查看如果在属性顶部添加了 JsonIgnore。你希望你的测试通过,我该怎么做。

Please feel free to tell me that this is not practical at all, but still would be nice to know.

请随时告诉我这根本不实用,但还是很高兴知道。

[EDIT] The above test succeeds with the following older json-path dependencies:

[编辑] 上面的测试通过以下较旧的 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>

[EDIT] Found a quickfix that works with latest version of jayway.jasonpath after reading the documentation of spring's json path matcher.

[编辑] 在阅读了 spring 的 json 路径匹配器的文档后,找到了一个适用于最新版本的 jayway.jasonpath 的快速修复程序。

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

采纳答案by benezra444

I had the same problem with the newer version. It looks to me that the doesNotExist() function will verify that the key is not in the result:

我在新版本中遇到了同样的问题。在我看来,doesNotExist() 函数将验证密钥不在结果中:

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

回答by Paolof76

@JsonIgnore is behaving as expected, not producing the password in the json output, so how could you expect to test something that you are explicitly excluding from the output?

@JsonIgnore 的行为符合预期,未在 json 输出中生成密码,那么您怎么能期望测试您明确从输出中排除的内容?

The line:

线路:

.andExpect(jsonPath("$.property", is("some value")));

or even a test that the property is null:

甚至测试该属性为空:

.andExpect(jsonPath("$.property").value(IsNull.nullValue()));

correspond to a json like:

对应于一个json,如:

{
...
"property": "some value",
...
}

where the important part is the left side, that is the existence of "property":

其中重要的部分是左侧,即“财产”的存在:

Instead, @JsonIgnore is not producing the porperty in the output at all, so you can't expect it not in the test nor in the production output. If you don't want the property in the output, it's fine, but you can't expect it in test. If you want it empty in output (both in prod and test) you want to create a static Mapper method in the middle that is not passing the value of the property to the json object:

相反,@JsonIgnore 根本没有在输出中产生属性,所以你不能指望它不在测试中也不在生产输出中。如果您不想要输出中的属性,那很好,但是您不能期望它在测试中。如果您希望它在输出中为空(在生产和测试中),您希望在中间创建一个静态 Mapper 方法,该方法不将属性值传递给 json 对象:

Mapper.mapPersonToRest(User user) {//exclude the password}

and then your method would be:

然后你的方法是:

@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);
    }
}

At this point, if your expectations are for Mapper.mapPersonToRest to return a user with a null password, you can write a normal Unit test on this method.

此时,如果您期望 Mapper.mapPersonToRest 返回一个密码为空的用户,您可以在此方法上编写一个普通的单元测试。

P.S. Of course the password is crypted on the DB, right? ;)

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

回答by M.Sinan ?ahin

doesNotHaveJsonPathfor checking that it is not in json body

doesNotHaveJsonPath用于检查它是否不在 json 正文中