문제 사항
컨트롤러에서 다음과 같이 redirectAttributes 로 리다이렉트를 한다.
직접 Postman으로 실행하면 정상적으로 리다이렉트 되지만
MockMVC로 테스트를 하면 {key} 부분을 동적으로 변환을 하지 못해 실패를 한다.
에러 내용
고민한 방법
디버깅
먼저 key가 null로 들어가는 것이 확인 된다.
RequestMappingHandlerAdapter 클래스에서 view 와 rediretModel 이 mavContainer에 담겨있는 것을 확인
DispatcherServlet 클래스까지 잘 전달되는게 확인 (물론 key는 null이라 이 부분을 확인해야한다)
그런데 DisPatcherServlet 클래스내에서 processDispatchResult 메서드가 실행된 후 예외 발생
Mock 테스트 내에서 문제가 발생한 것은 맞지만 예상대로 동적으로 값을 못넣는다거나 그런건 아닌 것 같고 아까 key에 null이 들어간 것 부터 확인이 필요한 것 같다.
문제의 원인은 여기서 발생했다.
mockito의 when 메서드 인자에 projectService.createProject(request)가 들어오면 project 를 반환해라는 의미이다.
먼저 OngoingStubbing 클래스를 통해 InnovocationContainerImpl에 request 클래스의 주소를 등록하고 return값도 등록해놓는다.(answer변수에 "project" 등록)
이제 api를 수행하다 createProject메서드를 만나면 MockHandlerImpl의 handler() 메서드 에서 값을 비교하기 시작한다.
먼저 같은 메서드인지 확인하고 인자를 매칭하는 것으로 보인다. argumentMatch(candidate)
좀만 더 깊게 들어가보면 이제 ArgumentMatcher(여기에는 InnovocationContainerImpl에 request 클래스의 주소을 등록한 matcher)와 인자값으로 들어온 argument(즉 request)를 비교하기 시작한다. 아래는 먼저 같은 클래스인지 부터 비교한다. 당연히 이건 true을 반환한다(둘 다 ProjectCreateRequest 이므로)
이제 오른쪽 matches 메서드를 실행하면 Equality의 areEqual메서드로 판단을 하는데,
빨간박스친 부분이 실행된다.
즉, 인자에 들어가는 객체는 Mockito에서 when().thenReturn을 사용할때 메서드의 파라미터로 넘어오는 클래스까지 equals를 사용하여 비교한다.
Request 객체 일치 문제: Mockito에서는 when(...) 구문에 전달된 인수와 실제 메서드 호출 시 사용된 인수가 정확히 일치해야 한다.
만약 인수가 객체인 경우, 객체의 동등성,동일성(equals)이 판단된다. projectService.createProject(request) 호출(perform실행)에 사용된 request 객체가 when(...) 구문에 사용된 객체와 정확히 일치하지 않는다면, Mockito는 설정된 반환 값을 무시하고 기본값인 null을 반환한다.
해결방법
다음과 테스트코드를 작성해도 되지만
when(projectService.createProject(any(ProjectCreateReq.class))).thenReturn("project");
dto에 equals 를 오버라이딩을 하여 객체간 값만 일치해도 동일성을 확보하는 습관을 갖는게 좋을 것 같다.
번외
추가적으로 왜 when절에 넣은 request와 perform에 수행되는 request와 같은 request인데 결국엔 주소값이 다른 객체로 변하는지 의문이 들 것이다.
나도 그 부분이 의아해서 다시 시간내서 찾아봤다.
아래는 perform 메서드 도중 수행되는 로직 중 HandlerMethodArgumentResolver 부분이다.
HandlerMethodArgumentResolver는 요청 데이터를 메서드의 매개변수로 변환할 때 사용하는 전략 인터페이스 이다.
다시 말해서 어떠한 요청이 컨트롤러에 들어왔을 때 요청 데이터로 부터 원하는 객체를 반환하는 역할을 수행한다.
다시 아래 코드를 보자
objectMapper을 통해 request를 직렬화하여 바이트로 변환한다음 해당 url을 요청한다.
그리고 나서 아까 resolveArgument 부분에서 HandlerMethodArgumentResolver의 resolveArgument 메서드를 요청한다.
jakson의 objectMapper로 직렬화한 객체의 HandlerMethodArgumentResolver 구현체는 RequestResponseBodyMethodProcessor 이므로 해당 부분에 브레이크포인트를 걸면
바이트스트림이 객체로 변환되었음을 알 수가 있다.
결론적으로 같은 객체가 아님을 확인했다. 따라서 반드시 equals를 오버라이딩해서 값만 같아도 동일성을 확보하던지 mokito의 any절을 활용해야 해당 문제를 해결할 수가 있다!
'toy > AgileHub' 카테고리의 다른 글
[이슈 #1] 커넥션 풀 고갈 문제를 Redis Atomic 연산으로 개선하기 (3) | 2024.12.01 |
---|---|
이슈 번호 생성의 동시성 문제와 커넥션 풀 최적화 여정 (2) | 2024.11.09 |
이슈 전체 조회 성능 개선하기 (0) | 2024.07.06 |
배포 하는데 걸리던 시간 13분을 5분으로 줄이기 (0) | 2024.05.14 |
docker volume 제대로 설정하자 (0) | 2024.05.09 |