LocalDateTime의 Json 직렬화 포맷 VS LocalDateTime의 jsonPath().value() 통과 포맷

상우·2024년 12월 29일

상황


@WebMvcTest(PolicyController.class)
@MockBean(JpaMetamodelMappingContext.class)
class PolicyControllerTest {

    @MockBean
    private PolicyService policyService;

    private int port = 8080;

    @Autowired
    private ObjectMapper objectMapper;

    @Autowired
    private MockMvc mockMvc;

    private static AddRatePolicyForBookRequest addRatePolicyForBookRequest;
    private static AddRatePolicyForCategoryRequest addRatePolicyForCategoryRequest;
    private static AddRatePolicyForBookResponse addRatePolicyForBookResponse;
    private static AddRatePolicyForCategoryResponse addRatePolicyForCategoryResponse;

    @BeforeAll
    static void setUp() throws NoSuchFieldException {

        // 정률정책 for Book 추가 요청
        addRatePolicyForBookRequest = new AddRatePolicyForBookRequest();
        for(Field field : addRatePolicyForBookRequest.getClass().getDeclaredFields()){
            field.setAccessible(true);
        }
        ReflectionUtils.setField(
                addRatePolicyForBookRequest.getClass().getDeclaredField("discountRate")
                ,addRatePolicyForBookRequest
                , 10);
        ReflectionUtils.setField(
                addRatePolicyForBookRequest.getClass().getDeclaredField("minimumOrderAmount")
                ,addRatePolicyForBookRequest
                , 30000);
        ReflectionUtils.setField(
                addRatePolicyForBookRequest.getClass().getDeclaredField("maximumDiscountPrice")
                ,addRatePolicyForBookRequest
                , 10000);
        ReflectionUtils.setField(
                addRatePolicyForBookRequest.getClass().getDeclaredField("expirationPeriodStart")
                ,addRatePolicyForBookRequest
                , LocalDateTime.of(2024,1,1,12,0));
        ReflectionUtils.setField(
                addRatePolicyForBookRequest.getClass().getDeclaredField("expirationPeriodEnd")
                ,addRatePolicyForBookRequest
                , LocalDateTime.of(2024,1,10,12,0));
        ReflectionUtils.setField(
                addRatePolicyForBookRequest.getClass().getDeclaredField("name")
                ,addRatePolicyForBookRequest
                , "테스트용 정책");
        ReflectionUtils.setField(
                addRatePolicyForBookRequest.getClass().getDeclaredField("description")
                ,addRatePolicyForBookRequest
                , "테스트용 정책 설명");
        ReflectionUtils.setField(
                addRatePolicyForBookRequest.getClass().getDeclaredField("bookId")
                ,addRatePolicyForBookRequest
                ,1L);
        ReflectionUtils.setField(
                addRatePolicyForBookRequest.getClass().getDeclaredField("policyStatusId")
                ,addRatePolicyForBookRequest
                ,0);

        // 정률정책 for Book 추가 응답
        addRatePolicyForBookResponse = new AddRatePolicyForBookResponse(
                10,
                30000,
                10000,
                LocalDateTime.of(2024,1,1,12,0),
                LocalDateTime.of(2024,1,10,12,0),
                "테스트용 정책",
                "테스트용 정책 설명",
                1L,
                0
        );
    }


    @Test
    @DisplayName("정룰정책 for Book 요청시 - 200과 응답이 잘 돌아오는지 확인")
    void addRatePolicyForBookTest() throws Exception {

        Mockito.when(policyService.addRatePolicyForBook(Mockito.any(AddRatePolicyForBookRequest.class)))
                .thenReturn(addRatePolicyForBookResponse);

        String url = "http://localhost:"+port +"/task/policies/rate/book";

        String req = objectMapper.writeValueAsString(addRatePolicyForBookRequest);

        mockMvc.perform(post(url).header("X-MEMBER-ID",1L)
                .contentType(MediaType.APPLICATION_JSON)
                .content(req))
                .andExpect(jsonPath("$.discountRate").value(10))
                .andExpect(jsonPath("$.minimumOrderAmount").value(30000))
                .andExpect(jsonPath("$.maximumDiscountPrice").value(10000))
                .andExpect(jsonPath("$.expirationPeriodStart")
                        .value(LocalDateTime.of(2024,1,1,12,0,0)))
                .andExpect(jsonPath("$.expirationPeriodEnd")
                        .value(LocalDateTime.of(2024,1,10,12,0,0)))
                .andExpect(jsonPath("$.name").value("테스트용 정책"))
                .andExpect(jsonPath("$.description").value("테스트용 정책 설명"))
                .andExpect(jsonPath("$.bookId").value(1L))
                .andExpect(jsonPath("$.policyStatusId").value(0));
    }

다음과 같이 컨트롤러에 대한 테스트코드를 작성중이었다.
어떤 req를 보냈을때 , 어떤 resp를 기대하는지에 대한 코드였다
request를 보내게 된다면,
response는 request의 필드에 담겨있는 값들을 그대로 가지고 돌아오게 되어 있었다.

그래서

요청의 "expirationPeriodStart" 에
LocalDateTime.of(2024,1,1,12,0) 을

 ReflectionUtils.setField(
                addRatePolicyForBookRequest.getClass().getDeclaredField("expirationPeriodStart")
                ,addRatePolicyForBookRequest
                , LocalDateTime.of(2024,1,1,12,0));

응답의 기댓값 역시
LocalDateTime.of(2024,1,1,12,0)으로 설정해주었다

       .andExpect(jsonPath("$.expirationPeriodStart")
                        .value(LocalDateTime.of(2024,1,1,12,0,0)))

문제

그리고 테스트 코드를 실행하니 문제가 발생하였다.

java.lang.AssertionError: JSON path "$.expirationPeriodStart" expected:<2024-01-01T12:00> but was:<2024-01-01T12:00:00>
Expected :2024-01-01T12:00
Actual   :2024-01-01T12:00:00

LocalDateTime.of(2024,1,1,12,0)을 넣어서
기대값으로 설정된 값은 2024-01-01T12:00 이었는데

LocalDateTime.of(2024,1,1,12,0)을 넣어서
request로 날아온 실제 값은 2024-01-01T12:00:00 이었다.

원인

원인은
LocalDateTime이 직렬화 될때와
LocalDateTime이 jsonPath의 value() 메서드를 통과할때
변환되는 형태가 다르기 때문이었다.

LocalDateTime이 직렬화 될 때에는
jackson이 LocalDateTime을 직렬화할 때 항상 전체 포맷(초 단위 포함)을 사용한다

반면에
LocalDateTime이 jsonPat의 value 메서드를 통과할때에는
ISO_LOCAL_DATE_TIME 포맷의 축약 버전으로 변환된다고 한다
(초 단위가 0일 경우 생략).

그래서 형태가 다르고 일치하지 않는것이었다

해결

기댓값의 응답 형태를 직접 입력해주는것으로 바꿔주었다

 .andExpect(jsonPath("$.expirationPeriodStart")
                        .value("2024-01-01T12:00:00"))
profile
엉성해도 우직하게

0개의 댓글