Hits
SpringBoot 2022. 10. 1. 오전 8:06:00

[오삽내]BDDMockito then 메소드 오류

"repositoy mocking 테스트에서 발생한 오류 해결"
spring-boottestmockito

오늘의 삽질 내용

BDDMockito.then() 사용중 오류 발생

오늘의 문제

Service Layer 유닛테스트를 위해 repository를 mocking하고 BDDMockitothen 메소드를 사용해서 repository가 기대하는대로 호출되는지 테스트를 했습니다. 이 때 save메소드를 테스트할 때 오류가 발생했습니다. 간단한 방법으로 문제를 해결했지만 검색으로 답을 찾지 못해 제 경우의 해결방법을 정리해보려합니다.

오류 메세지

Wanted but not invoked:
clipBucketRepository.save(
    package.path.ClipBucket@34e2026b
);
-> at package.path.ClipServiceTest.createClipBucket(ClipServiceTest.java:94)

However, there was exactly 1 interaction with this mock:
clipBucketRepository.save(
    package.path.ClipBucket@24361cfc
);
-> at package.path.ClipService.createClipBucket(ClipService.java:47)

오류 발생 코드

이때 문제의 테스트 코드는 아래와 같았습니다.

@ExtendWith(MockitoExtension.class)
class ClipServiceTest {

    @InjectMocks
    private ClipService clipService;

    @Mock
    private ClipBucketRepository clipBucketRepository;
    @Mock
    private UserRepository userRepository;

        @Test
    @DisplayName("클립통 생성")
    void createClipBucket() {
        //given
        User owner = User.builder().username("testOwner").build();
        ClipBucket bucket = new ClipBucket("testBucket", owner);
        given(userRepository.getReferenceById(1L)).willReturn(owner);
        given(clipBucketRepository.save(any(ClipBucket.class))).willReturn(bucket);

        //when
        ClipBucket result = clipService.createClipBucket("testBucket", 1L);

        //then
        assertThat(result.getName()).isEqualTo("testBucket");
        then(userRepository).should(times(1)).getReferenceById(1L);
        then(clipBucketRepository).should(times(1)).save(bucket); // 문제의 코드
    }
}

해결 과정

에러코드를 바탕으로 검색해봐도 @Mock대신 @Spy를 쓰라는 답변 정도만 찾았을 뿐 마땅한 해법을 찾기가 어려웠습니다. 문제의 코드에 bucket 대신 result를 넣어봐도 똑같은 문제만 발생을 했습니다. 평소 습성대로 대충 야매로 구글링 긁적긁적해서 훑어보고 적용해봤는데 역시나 안되는구나 싶었습니다. 문제의 코드만 지우면 돌아가긴 하는데 이대로 넘기면 앞으로도 쓰기 어려울것 같고 테스트코드에 구멍이 난거같아 찝찝해서 될때까지 붙잡고 뜯어보았습니다. 서비스 클래스에서 동작하는대로 집어넣어야하나 싶어서 new ClipBucket()을 넣어봐도 해결은 안되더군요. 뭐가문제인가 하고 에러메세지를 곰곰히 들여다보면서 그제서야 내용을 이해하려고 시도해보았습니다.

Wanted but not invoked → 필요한데 호출이 안됨
However, there was exactly 1 interaction with this mock → 근데 이 mock에 한개의 상호작용이 있음

mock? repository mocking에 문제가 있을 것 같지는 않고, 어디서 일어나는건가 하고 보다보니 given메소드에서 method mock한게 보이더라구요. 설마? 싶어서 save(any(ClipBucket.class))로 바꿔주니 정상적으로 동작했습니다.

결론

then메소드에서는 메소드 행동에 대한 예상 코드를 작성해서 테스트를 하는데 given으로 mocking한 결과값을 실제 코드라고 예측해서 서로 충돌이 일어난 것 같습니다. given을 통해 repository 동작을 mocking 했으면 then에서도 동일한 메소드를 반환해줍시다.