web-dev-qa-db-ja.com

JSONパスに特定の要素が含まれていないか、または要素が存在する場合はnullであるかどうかをテストするにはどうすればよいですか?

私は、単純なSpring Webアプリケーション用のいくつかの単純な単体テストルーチンを書いています。リソースのゲッターメソッドに@JsonIgnoreアノテーションを追加すると、結果のjsonオブジェクトには対応するjson要素が含まれません。したがって、ユニットテストルーチンがこれがnullであるかどうかをテストしようとすると(これは私のケースで予想される動作であり、パスワードをjsonオブジェクトで利用できないようにしたい)、テストルーチンは例外になります:

Java.lang.AssertionError:JSONパスの値がありません:$ .password、例外:パスの結果がありません:$ ['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リソースアセンブラを使用しています、これは私のリソースクラスです:

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

私はこれがなぜ例外を受け取っているのかを理解しています。また、ある意味で、パスワードフィールドを見つけることができなかったというテストは成功しています。しかし、私がやりたいのは、このテストを実行して、フィールドが存在しないか、存在する場合はnull値が含まれていることを確認することです。どうすればこれを達成できますか?

スタックオーバーフローにも同様の投稿があります: MockMvcを使用したHamcrest:キーが存在するが値が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())
56
Zobayer Hasan

新しいバージョンでも同じ問題が発生しました。 doesNotExist()関数がキーが結果にないことを検証するように見えます:

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

@JsonIgnoreはjson出力でパスワードを生成せず、期待どおりに動作しているので、出力から明示的に除外しているものをテストするにはどうすればよいでしょうか?

この線:

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

または、プロパティがnullであることをテストします。

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

次のようなJSONに対応します。

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

ここで重要な部分は左側、つまり「プロパティ」の存在です。

代わりに、@ JsonIgnoreは出力でporpertyをまったく生成していないため、テストでも実動出力でもないことを期待することはできません。出力にプロパティが必要ない場合は問題ありませんが、テストでは期待できません。出力(prodとtestの両方)で空にする場合は、プロパティの値を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がnullパスワードを持つユーザーを返すことが期待される場合、このメソッドで通常の単体テストを作成できます。

追伸もちろん、パスワードはDBで暗号化されますよね? ;)

3
Paolof76