최근에 프로젝트를 진행하며 2주동안 해결하지 못했던 문제가 있었습니다.
service 단위 테스트를 작성하는데 mockito 를 사용하여 repository를 mock 처리하고 service에 insertMock 하였습니다.
단위 테스트에서 테스트 메서드를 하나씩 작성할 때 마다 하나씩 실행시키며 잘되는 것을 확인하며 진행하였습니다.
service의 특정 메서드 테스트를 모두 작성한 뒤 한번에 실행시켰더니 일부 테스트가 실패했습니다.
테스트를 주석 처리하고 하나씩 실행시키면 다시 정상적으로 작동하였습니다.
실패할때는 모킹한 객체가 doReturn 으로 설정한대로 동작하지 않았습니다.
이는 간단하게 확인할 수 있었습니다.
디버깅을 통해 service에서 사용하는 repository를 확인해보니 모킹된 객체임을 확인할 수 있었습니다.
repository가 doReturn 한대로 동작하는지 확인하기 위해 테스트 코드에서 모킹된 repository를 검증하였고 정상적으로 동작함을 확인할 수 있었습니다.
repository에서 findById 메서드를 호출하는데 상황에 따라 비어있는 optional을 리턴하기도 하고 비어있지 않은 optional을 리턴하도록 하고 있습니다.
둘다 any()로 argument를 받고 있어서 상황에 따라 구분이 안되는 것이 아닌가 생각되어 any() 대신 eq(1L), eq(2L) 과 같은 식으로 구별할 수 있도록 수정하였으나 해결되지 않았습니다.
테스트 코드에서 서비스와 레포지토리는 테스트 최상위 클래스에서 선언하여 해당 테스트에서 모두 공유하고 있었습니다.
때문에 각 인스턴스가 공유되어 독립성이 보장되지 않는 문제일 수 있다고 생각되어 각 테스트 메서드를 내장 클래스로 만들고 각각 인스턴스를 선언하여 사용하도록 수정하였더니 테스트를 모두 통과하였습니다.
그러나 각 메서드를 별도의 클래스로 감싸고 각각 인스턴스를 선언해서 사용하는 방식은 유지보수에 굉장히 문제가 많은 방식입니다.
더 좋은 방법을 찾기 위해 많은 예시 코드를 찾아보았지만 모두 별도의 클래스로 감싸지 않아도 잘 작동하였습니다.
다른 예시 코드와 차이가 없는데 제 코드만 문제가 발생하는 것이 억울하였습니다.
일단 작성한 테스트 코드를 두고 새로운 테스트 코드 파일을 생성하여 처음부터 작성해보았습니다.
다시 작성하였더니 신기하게도 인스턴스를 공유해도 문제가 없었습니다.
제가 @BeforeAll을 사용하기 위해 @TestInstance(LifeCycle.PER_CLASS)를 사용했습니다.
때문에 라이프 사이클이 각 클래스 단위로 설정되어 인스턴스가 독립적이지 않고 공유하게 되었습니다.
해당 어노테이션을 삭제하고 beforeAll을 삭제하였더니 테스트가 모두 통과하였습니다.
해결하여 기쁘고 너무 간단한 원인이라 허탈했습니다.
문제가 해결이 안될때는 어노테이션도 잘 살피고
예시와 같은데 왜 안되지?? 할때는 처음부터 작성해보기