ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Spock 개관
    framework 2020. 5. 7. 18:46
    반응형

    Spock은 Groovy문법을 사용한 단위 테스트 프레임워크입니다. Spock은 다른 테스트 프레임워크에 비해 시간을 아낄 수 있고 Stubbing, Mocking, Spying 작업들을 아주 간단한 코드로 할 수 있습니다. 또한 Spock는 요즘 핫한 BDD에 적합한 프레임워크라고 합니다. 오늘은 이처럼 다양한 장점을 가진 Spock을 간단하게 살펴보도록 하겠습니다.

     

        def "나이가 30살이다"() {
            given:
                def age
            when:
                age = 29
            then:
                30 == age
        }

     

    위 코드는 잘 못된 코드이기 때문이기 때문에 테스트가 실패합니다. 이 때 Spock는 명확하고 아름답게 오류 메세지를 표시해 줍니다. 이것은 Spock을 사용하는 또 다른 이유입니다.

     

    Condition not satisfied:
    
    2 == age
      |  |
      |  29
      false

     

    다시 위의 코드로 돌아가서 코드의 각 부분을 살펴봅시다. 일단 메서드의 이름을 따옴표 안에 표현 가능하다는 것을 알 수 있습니다. 이를 통해 보다 쉽고 간편하게 테스트 코드의 목적을 표현할 수 있습니다. 

     

    메서드 안의 코드는 세 부분으로 나뉘어지는 것을 알 수 있는데. 실제 테스트 하고자 하는 기능을 지정하는 given, 실제로 테스트를 진행하는 when, 마지막으로 결과를 확인하는 then입니다. BDD에 대해 알고 있는 독자라면 이 구분된 섹션들의 배열이 BDD와 매우 어울린다는 것을 알 수 있을 것입니다.

     

    이제 Spock가 가진 기능들로 눈을 돌려 보겠습니다. 먼저 Stub을 살펴볼까요? Stub을 생성하기 위해서는 Spock코드 내에서 Stub() 메서드를 호출하면 됩니다. 

     

    def "리스트의 사이즈는 3이다"() {
       given:
          List list = Stub()
          list.size() >> 3
       expect:
          // let's see if this works
          list.size() == 3
    }
    

     

    기본적으로 Stub을 사용하여 수행하는 작업은 Stub화 된 클래스의 메서드가 호출 될 때 발생하는 작업을 정의 하는 것입니다. 위 코드에서 >> 연산자를 사용해서 Stub화 된 메소드의 리턴값을 3으로 지정하고 있습니다. 만약 예외 등과 같은 사이드 이펙트를 원한다면 다음과 같이 하면 됩니다.

     

    def "리스트의 사이즈를 호출하면 예외를 던진다"() {
       given:
          List list = Stub()
          list.size() >> { throw new IllegalStateException() }
    }

     

    이제는 또다른 기능인 Mock을 살펴보겠습니다. Mock역시 Mock() 메서드를 통해 호출 할 수 있습니다.

     

    def "사이즈 메소드는 한 번만 실생된다"() {
        given:
            List list
        when:
            list = Mock()
        then:
            1 * list.size()
    }

     

    Mock은 인터페이스의 관계를 확인하기 보다는 상호작용을 확인하기 위해 자주 사용합니다. 위 코드에서 then 섹션에서 상호작용 여부를 확인할 수 있습니다. size 메소드가 한 번 호출되었다고 했지만 사실은 한 번도 호출된 적이 없기 때문에 이 테스트는 실패하게 됩니다.

     

    상호작용의 발생 횟수 뿐 아니라 순서 역시 확인할 수 있습니다. 

     

    def "커밋 전에 저장 되어야 한다"() {
        given:
            UserService service = Mock()
            Transaction transaction = Mock()
        when:
            service.save(new User())
            transaction.commit()
        then:
            1 * service.save(_ as User)
        then:
            1 * transaction.commit()
    }

     

    이제 Spy에 대해 알아보겠습니다. Spy가 Stub이나 Mock과 다른것은 Spy는 더미개체가 아니고 일반 개체에 대한 래퍼라는 것입니다. Spy를 생성할 때 이 점을 유의해야 합니다.

     

    def "save 메소드를 호출한다"() {
        given:
            Transaction transaction = Stub(Transaction)
            UserService service = Spy(UserServiceImpl, constructorArgs: [transaction])
        when:
            service.save(new User(name: 'Katherine'))
        then:
            1 * service(_)
    }

     

    Mock과 Spy가 다른 점은 Spy가 실제 호출되는 메소드의 동작을 변경하지 않았다는 것입니다. 특정 멧드가 호출되었는지 확인하고싶을 때 주로 Spy를 사용하게 됩니다.

     

    지금까지 아주 간단하게 Spock을 알아봤습니다. 이 포스트의 내용은 상당부분 https://thejavatar.com/testing-with-spock/ 의 내용을 참고했음을 밝힙니다.

    반응형
Designed by Tistory.