본문 바로가기
개발/Development

[Test - Spock + Kotlin] Mocking 실패 문제

by 달사쿠 2021. 7. 14.
반응형

Spring Boot + Spock Framework + Kotlin 조합으로 UnitTest 코드를 작성하다 문제를 마주하게 되었다.

이 문제는 재직중인 회사에서 tc를 작성하던 중 발생한 문제로, 소스코드를 그대로 담을 수 없어 최대한 비슷한 코드를 따로 작성해보았다.

 

결론부터 말하자면, 나와같은 환경, 상황이 발생한다면 Spock Framework를 사용하여 test를 진행하기 어려울 것 같다 ㅠㅠ

 

일단 문제가 발생한 코드는 아래와 같다.

class HelloTest extends Specification {
    HelloDao helloDaoMock
    HelloService helloService 
    
    def setup() {
    	helloDaoMock = Mock(HelloDao) 	//문제 발생
        helloService = new HelloService(helloDaoMock)
   }
   ...
}

위 테스트를 실행한 결과, CannotCreateMockException 에러가 발생하였다.

org.spockframework.mock.CannotCreateMockException: Cannot create mock for class dao.HelloDao because Java mocks cannot mock final classes. If the code under test is written in Groovy, use a Groovy mock.

일단 기본적으로 Kotlin 클래스의 경우, class에 별다른 키워드를 붙이지 않는 경우 final 클래스로 클래스가 생성되게 된다.

Groovy의 경우, Mockito와 비슷한 형태를 띄고 있고 Mockito의 Mock 또한 final class를 mocking 할 수 없는 문제를 갖고 있기 때문에 Groovy를 사용하고 있으면 Groovy mock을 사용하라고 친절히 알려 준 것 같다.

 

일단 에러 메세지를 토대로 Mock을 GroovyMock으로 변경하고 아래와 같이 작성된 코드를 돌린 결과,,

class HelloTest extends Specification {
    HelloDao helloDaoMock
    HelloService helloService 
    
    def setup() {
    	helloDaoMock = GroovyMock(HelloDao){
        	getHelloText(_) >> "HELLO WORLD"
        }
        helloService = new HelloService(helloDaoMock)
   }
   
   def "getHelloText test: Success"() {
       when:
       helloService.getHelloText("HELLO WORLD")
       
       then:
       1 * helloDaoMock.getHelloText("HELLO WORLD")
   }
}

 

이번엔 또 다른 문제가 발생하였다.. 아래의 에러를 요약하자면 분명 HelloDao를 mocking하고, getHelloText 메소드를 stubing까지 했는데 stubing안의 내부 로직을 타는 것이다. 

java.lang.NullPointerException
     at dao.HelloDao.existText(HelloDao.kt:61)
     at dao.HelloDao.getHelloText(HelloDao.kt:139)
     at service.hello.HelloService.getHelloText(HelloService.kt:31)

조금 더 상황을 설명하자면 HelloDao의 경우 Spring dependency를 떼내기 위해 따로 Spring Anotation을 붙이지 않았고, 생성자에서 또 다른 class를 의존성을 주입해 사용을 했다.

 

이 문제와 관련해서 Stackoverflow와 여러 잡다한 해외 블로그를 찾아서 @TestConfiguration을 사용해서 mocking도 해보고 여러 가지 방법을 사용해본 결과 뭘 해도 안되었는데, 뭔가 Kotlin의 class가 final로 만들어진다는 점에서 class HelloDao를 open class HelloDao로 변경하고, getHelloText 메서드를 open fun getHelloText로 변경한 결과 Test를 통과할 수 있었다..

 

사실 이렇게 테스트 만을 위해 open class와 open method로 변경하는 것은 문제가 있다고 판단이 들어, Spock 프레임워크를 사용하는 것을 포기하고 Kotest를 사용해서 test code를 짜보기로 했다.

 

사실 지금도 어떤 부분에서 문제가 일어나는지 명확한 이유는 모르겠다. final class라는 이유이기에는 이전 프로젝트에서 spock를 이용해서 작성했던 tc의 경우 잘 돌아갔기 때문이다.

해당 tc와 이번 문제가 발생하는 tc의 차이점의 경우, Spring Component Annotation을 붙이고 안붙이고의 차이인데 이걸로 인해서 발생하는 문제일까,,? 라는 생각도 든다.

 

더불어 Spock 프레임워크가 정말 test code를 짜기에 굉장히 직관적이어서 선호를 했는데 아래의 블로그를 보고 Kotlin을 사용할 때는 Spock 사용하는 것을 조금 고민해보아야겠다는 생각도 했다. (대신 Kotest라는 좋은 test framwork를 알았다!!)

https://veluxer62.github.io/explanation/comparing-testing-library-for-kotlin/

 

Comparing Testing Library for Kotlin

Test의 중요성이 부각되면서 요즘 개발을 할때 테스트 코드를 많이 작성한다. 어쩌면 오퍼레이션 코드보다 테스트 코드가 프로젝트에서 더 많이 작성되기도 할지 모르겠다. 테스트 코드는 오퍼

veluxer62.github.io

 

 

혹시 제가 발생한 문제에 대해서 이유, 해결법을 알고계신 분이 있다면 알려주시면 정말 감사하겠습니다 :)

반응형

'개발 > Development' 카테고리의 다른 글

multi-part file 413, 501 error 해결하기  (0) 2021.03.23

댓글