source

JSON 경로에 특정 요소가 포함되어 있지 않은지 또는 요소가 존재하는지 여부를 테스트하려면 어떻게 해야 합니까?

goodcode 2022. 9. 4. 15:00
반응형

JSON 경로에 특정 요소가 포함되어 있지 않은지 또는 요소가 존재하는지 여부를 테스트하려면 어떻게 해야 합니까?

간단한 스프링 웹 애플리케이션을 위한 간단한 유닛 테스트 루틴을 작성하고 있습니다.리소스의 getter 메서드에 @JsonIgnore 주석을 추가하면 결과 json 객체에 대응하는 json 요소가 포함되지 않습니다.따라서 유닛 테스트 루틴이 null인지 테스트하려고 할 때(내 경우 예상되는 동작으로 비밀번호를 json 개체에서 사용할 수 없도록 합니다), 테스트 루틴에서 예외가 발생합니다.

java.displaces를 클릭합니다.Assertion Error: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;
    }
}

이것이 왜 예외가 되는지 알고 있습니다.또한 테스트에 성공했기 때문에 패스워드 필드를 찾을 수 없었습니다.다만, 이 테스트를 실행하고, 필드가 없는 것을 확인합니다.존재하는 경우는 늘 값을 포함합니다.어떻게 하면 좋을까요?

스택 오버플로에도 같은 투고가 있습니다.Hamcrest with MockMvc: 키가 존재하지만 값이 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>

잘 부탁드립니다.

[EDIT] 좀 더 정확하게 말하면, 예를 들어, 일부 필드가 null이거나 비어 있거나 존재하지 않아야 한다는 것을 알고 있는 엔티티에 대해 테스트를 작성해야 합니다.또한 속성 위에 JsonIgnore가 추가되어 있는지 확인하기 위해 코드를 검토하지 않습니다.그리고 당신은 당신의 테스트가 통과하기를 원하지만, 내가 어떻게 이것을 할 수 있나요?

이것은 전혀 실용적이지 않지만, 그래도 알고 싶습니다.

[EDIT] 위의 테스트는 다음과 같은 오래된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] spring의 json path matcher 문서를 읽고 최신 버전의 jayway.jasonpath와 연동되는 퀵픽스를 찾았습니다.

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

새로운 버전에서도 같은 문제가 있었습니다.doesNotExist() 함수는 키가 결과에 없는 것을 확인합니다.

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

, 그 성질이 있는 것은 다릅니다.null속성이 전혀 존재하지 않습니다.

다음 조건이 충족되지 않는 경우에만 테스트가 실패해야 하는 경우nulluse: 명령어, 명령어:

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

(「」를해도)nulluse: 명령어, 명령어:

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

@Json Ignore는 예상대로 동작하고 있고, json 출력의 패스워드는 생성되지 않았습니다.그러면 출력에서 명시적으로 제외된 것을 어떻게 테스트 할 수 있을까요?

행:

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

또는 속성이 null이라는 테스트도 마찬가지입니다.

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

다음과 같은 json에 대응합니다.

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

여기서 중요한 부분은 왼쪽이고, 그것은 "변형"의 존재이다.

대신 @JsonIgnore는 출력에 porpoty를 전혀 생성하지 않기 때문에 테스트나 생산 출력에 porpoty를 생성하지 않을 수 있습니다.출력에서 속성을 원하지 않으면 문제 없지만 테스트에서는 기대할 수 없습니다.출력(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가 늘비밀번호로 사용자를 반환할 것으로 예상되는 경우 이 메서드로 일반 유닛테스트를 작성할 수 있습니다.

추신: 물론 비밀번호는 DB에 암호화되어 있죠?;)

doesNotHaveJsonPath 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"));
  }

언급URL : https://stackoverflow.com/questions/32397690/how-to-test-if-json-path-does-not-include-a-specific-element-or-if-the-element

반응형