처음 테스트코드를 Spock를 이용하여 작성하려고 할 땐, 왜 @Mock객체를 스프링부트가 인식할 수 없는지 알지 못했다.
⭐️ [이전블로그보기]Spock 테스트프레임워크를 사용할 때 @Mock import안되는 문제
간단히 요약하자면 JPA Repository 인터페이스는 스프링 데이터 JPA에 의해 동적으로 구현되기 때문에 단순히 @Mock 어노테이션으로는 모킹할 수 없다. 따라서 추가적인 작업이 필요했는데 그것은 @SpringBootTest 어노테이션을 사용하여 테스트 클래스에서 스프링 컨텍스트를 로드하고, @MockBean 어노테이션을 사용하여 JPA Repository 인터페이스를 모킹하는 방법이었다.
@SpringBootTest
class UserControllerSpec extends Specification {
@Subject
UserController userController
@MockBean
UserRepository userRepository
...
...
...
}
하지만 이럴경우 OverloadingMethod를 Mocking 해야할 때 동적언어인 Groovy 특성상 어떤 메소드를 Mocking해야할 지 선택하지 못해서 Mocking실패로 테스트가 실패하는 문제가 발생한다. (참고하자면, Junit기반으로 변경해서 코드를 작성하면 테스트가 성공한다.)
Spock 테스트 프레임워크가 지원하는 Spring Mock으로 OverloadingMethod를 Mocking을 할 수 있도록 위의 문제를 해결할 수 있는데 이 또한, JPA에서 제공하는 JpaRepository 인터페이스를 사용하고 있다면 Mocking하지 못한다. 즉, JPA의
JpaRepository 인터페이스는 JPA의 영속성 컨텍스트와 상호작용하기 때문에 목 객체로 직접 생성할 수 없다는 뜻이다.
JPA가 cglib 프록시를 생성하면서 Spock 프레임워크가 Mock으로 생성한 프록시를 무시하기 때문에 Mocking이 되지 않고 JPA가 구현체를 생성해버리니 테스트가 항상 실패하고 마는 것이다.
따라서 JPA Repository를 사용하면서 Overloadiong된 Method를 사용하려면 테스트 환경설정을 하는 방법이 있다. @DataJpaTest 애노테이션을 사용하여 인메모리 데이터베이스를 설정하는 방법이다.
@DataJpaTest
class UserControllerSpec extends Specification {
@MockBean
DefaultUserService defaultUserService
UserController userController = new UserController(defaultUserService, /*mockReadOnlyUserService*/, /*mockCvService*/)
기존에 작성한 테스트 코드에서 @SpringBootTest 애너테이션을 지우고 @DataJpaTest을 사용한 코드로 수정하였다.
@DataJpaTest
@SpringBootTest