Spock으로 테스트코드를 작성한 경험을 공유합니다.

안녕하세요! 우아한형제들 배달의민족/배민라이더스 주문시스템 팀 정용준입니다.

여러분은 어떻게 테스트 코드를 작성하고 계신가요? 일정 지키기도 힘든데 무슨 테스트코드냐 하시는 분들도 계신가요?
저도 아직 테스트코드를 작성하는 것이 습관화되어 있지 않고 익숙하지 않습니다. TDD는 먼 나라 이야기이고 무엇을 어떻게 테스트해야 하는지를 정하기조차 어렵기도 하지만 조금씩 빈도를 늘려가고 있습니다.

저는 JUnit 기반의 테스트코드를 작성해왔습니다.
그러던 중 사내에서 Spock의 사용빈도가 높아지면서 좋은 점을 어필해주시는 주변 동료들 덕분에 Spock을 알게 되었는데요.
그동안 테스트코드를 작성하기도 쉽지 않을뿐더러 새로운 배움에 대한 부담감에 (일정도 빡빡하고 하다 보니) Spock에 도전해 볼 마음이 들지 않았습니다. 그런데 막상 테스트를 작성하다 보니 생각보다 간단했고 아직 Spock을 잘 모르시는 분과 함께 경험을 나누면 좋겠다고 생각이 들었습니다.

저도 이제 막 시작하는 단계이다 보니 부족한 점이 많습니다.
틀린 부분은 가감 없이 지적해주시고, 빈약한 내용은 열심히 공부해서 꼭 다음에 풍성히 채워오겠습니다.

Spock 시작하기

먼저 Spock을 사용하기 위해서 아래 두 의존성을 추가해야 합니다. (Gradle 기준)

testCompile('org.spockframework:spock-core:1.1-groovy-2.4')
testCompile('org.spockframework:spock-spring:1.1-groovy-2.4')

이제, Spock으로 테스트를 작성할 수가 있습니다.
Groovy

[Groovy 잘 몰라요, 그래도 일단 Go!]

Spock은 Groovy를 사용합니다. 사실 Groovy를 잘 알지 못한 점도 하나의 허들이었습니다.
이번 기회에 Groovy도 같이 공부할 수 있으니 좋네요 :)

‘소수점 버림’ 기능을 테스트 해보자!

public class CalculateTest {
    public static long calculate(long amount , float rate, RoundingMode roundingMode) {
        return BigDecimal.valueOf(amount * rate * 0.01)
                .setScale(0, roundingMode ).longValue();
    }
}

첫 번째 테스트

(초록막대가 나오길 두근두근..) spock_첫번째테스트_실패

[실패하였다..]

딸랑, 3줄 작성한 건데. (좀 당황스러웠습니다. ㅎㅎ)
저는 JUnit 테스트를 작성할 때 //given //when //then을 잊지 않기 위해 테스트 템플릿을 만들어 사용하고 있습니다.
이번에도 습관적으로 주석으로 명시하고 테스트를 돌려봤습니다. (나름 잘했다고 생각하고 있었는데..)
코드 블록을 선언하지 않은 것이 테스트가 깨지는 원인이었습니다.

block

Spock에서는 given, when, then과 같은 코드 블록을 block이라 부릅니다.
block은 테스트 메소드 (feature method) 내 최소한 하나는 있어야 하고요!
JUnit에서는 있어도 그만 없어도 그만이었는데 Spock에서는 필수입니다. (개인적으로는 더 좋네요!)

block [원본 출처] https://code.google.com/archive/p/spock/wikis/SpockBasics

  • given (또는 setup) : JUnit의 //Given처럼 테스트에 필요한 환경을 설정하는 작업. 항상 다른 블록 보다 상위에 위치해야함.
  • when : 테스트코드를 실행
  • then : 테스트코드 결과 검증, 예외 및 조건에 대한 결과를 확인할 수 있고 작성한 코드 한줄이 assert 문
  • expect : 테스트할 코드 실행 및 검증 (when + then)

드디어 성공!!

[드디어 성공!]

코드블록을 선언하고 나니 잘 수행이 되네요 :)

where blocks

사실, where block을 처음 보고나서 Spock을 써봐야겠다고 생각했습니다.
테스트를 하다보면 다양한 케이스를 검증해야 할 때가 많으시죠?

where_blocks

[그뤠잇!!]

where block을 사용하면 간단하게 해결할 수 있습니다.
처음 보는 생소한 기능이지만 코드를 따로 설명해 드리지 않아도 이해하는데 조금도 어려움이 없으실 거에요.

만약, JUnit 기반의 테스트코드를 작성했다면 어땠을까요?
JUnit_Test 의미없는 중복된 코드가 여기저기 널부러져 있었을 것 같습니다.
여러분은 이런 경우 어떻게 테스트코드를 작성하시나요?

그리고 테스트가 실패되는 경우 JUnit은 제일 처음 실패한 케이스만 알 수 있다면,
Spock은 실패한 모든 테스트 케이스와 그 내용을 더 상세히 알려줍니다. 친절한 Spock

[친절한 Spock씨]

예외 테스트

0보다 작은 음수가 들어왔을 때 ‘예외’가 발생하는지를 테스트 해보겠습니다.

thrown

Spock에서 예외는 thrown() 메서드로 검증할 수 있습니다.
thrown() 메서드는 발생한 예외를 확인할 수 있을 뿐만 아니라 객체를 반환하기 때문에 예외에 따른 메시지도 검증을 할 수 있습니다.
그리고 테스트코드를 작성한 흐름에 따라 예외를 확인할 수 있으니, 처음 코드를 본 사람이 더 쉽게 이해할 수 있을 것 같습니다 :)

Mock 테스트

thrown

Spock에서 Mock 테스트도 어렵지 않습니다.
가짜 객체의 반환 값은 ‘»‘으로 설정할 수 있고 예외를 발생시키고 싶다면 아래와 같이 하시면 됩니다.

orderSheet.getTotalOrderAmount() >> {throw new NegativeNumberNotAllowException()}

다음을 기약하며..

Spock으로 테스트코드를 작성하는 것 생각보다 어렵지 않은 것 같습니다.
아직은 작은 단위의 기능을 검증하는 정도로 적용하고 있는데요,
기존에 JUnit으로 작성한 테스트코드를 Spock으로 구현해봐야겠습니다.

다음번에는 좀 더 깊이 있는 내용으로 찾아뵙겠습니다.

참고자료