0%

Spock Testing

Spock Introduction

WHAT IS SPOCK?

  • Groovy 문법을 사용하는 단위 테스트 프레임워크

WHY SPOCK?

  • 간결하고 가독성이 좋은 테스트를 작성 가능
  • Mock 지원이 좋아서 사용이 편리
  • 그루비를 사용해서, 자바 보다 다양한 표현력 가능

Simple Test in Spock

먼저 Spock은 어떤것인지 어떻게 사용하는지 가볍게 살펴보자

1
2
3
4
5
6
7
8
def "리스트 첫번째 요소는 2를 리턴해야 한다"() {
given:
List<Integer> list = new ArrayList<>();
when:
list.add(1)
then:
2 == list.get(0)
}

기존에 JUnit을 사용하고 있던 유저라면 크게 이질적이지 않게 받아들여질법한 코드이다.
조금 더 감동적인 부분은 다음에 있다..
SimpleTest
테스트 실패시 출력되는 콘솔내용으로 오류를 파악하는데 용이하게 되어 있다.

Spock Basic

본격적으로 Spock 사용법을 알아보자

Dependency 추가

spock-core, 그리고 스프링을 사용한다면 spock-spring을 추가한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!-- Maven기준 pom.xml -->
<dependency>
<groupId>org.spockframework</groupId>
<artifactId>spock-core</artifactId>
<version>1.1-groovy-2.4</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.spockframework</groupId>
<artifactId>spock-spring</artifactId>
<version>1.1-groovy-2.4</version>
<scope>test</scope>
</dependency>

Spock Test 생성

spock.lang.Specification을 상속하기만 하면 된다

1
2
3
class SpockTest extends Specification {

}

Fixture Methods

테스트 전후처리에 사용되는 메소드들에 대한 정리

SPOCK JUnit
def setup() @Before
def cleanup() @After
def setupSpec() @BeforeClass
def cleanupSpec() @AfterClass

Feature Methods

실제 테스트에 사용하는 메소드는 아래와 같이 정의한다

1
2
3
def "메소드명"() {
// Blocks
}

Blocks

  • setup / given: 테스트에 필요한 객체나 환경 준비
  • when: 테스트할 코드를 실행
  • then: 예외/조건 확인, 모킹과 몇번 호출되었는지 확인 when과 꼭 함께 사용해야 한다
  • expect: then과 같으며, when을 필요로 하지 않기 때문에 간단한 테스트에 사용
  • where: parameterized 테스트용

@Shared

공유객체 사용을 위한 애노테이션

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Shared def sharedNumber = 0

def "SharedTest1"() {
when:
sharedNumber++
then:
1 == sharedNumber
}

def "SharedTest2"() {
when:
sharedNumber++
then:
2 == sharedNumber
}

Exceptions

익셉션 발생여부를 검증할 수 있다.
thrown()과 notThrown()을 사용한다.

1
2
3
4
5
6
7
def "ExceptionTest"() {
when:
throw new Exception("익셉션 발생 후 추가 작업 가능")
then:
Exception e = thrown()
e.getMessage() == "익셉션 발생 후 추가 작업 가능"
}

WHERE

테스트 로직은 동일하고, input값을 변경해 여러번 수행하고 싶을때 사용한다

1
2
3
4
5
6
def "Parameterized Test"() {
expect:
name.size() == length
where:
[name,length] << [['Game', 4], ['Portal', 6]]
}

아래와 같이 테이블 형식으로도 사용할 수 있다

1
2
3
4
5
6
7
8
def "Data Table"() {
expect:
name.size() == length
where:
name | length
'Game' | 4
'Portal' | 6
}

@Unroll

Iteration 결과를 독립적으로 확인 하고 싶을때 사용한다

1
2
3
4
5
6
7
8
9
10
@Unroll
def "#a와 #b 에서 더 큰 수는 #c 이다"() {
expect:
Math.max(a, b) == c
where:
a | b | c
1 | 2 | 2
3 | 4 | 4
6 | 5 | 5
}

Unroll

Mock

아래와 같은 방식으로 Mock을 생성할 수 있다

1
2
def list = Mock(List)
List list = Mock()

기대값 설정하는 방법을 알아보자

1
2
3
4
5
6
7
8
9
def "기대값 설정 테스트"() {
given:
List<String> list = Mock()
when:
def actual = list.get(0)
then:
list.get(0) >> 'GamePortal' // 기대값을 설정한다
actual == 'GamePortal'
}

기타

  • @Timeout : 타임아웃 설정 (단위 : 초)
  • @Ignore : 선택 테스트 무시
  • @IgnoreRest : 선택 외 무시
  • @FailsWith (value=Exception, reason=“에러메세지”)

느낀점

  • 올인원 느낌의 편리성이 있다(hamcrest, mockito등을 알필요가 없다)
  • 짧은시간에 익힐 수 있다
  • 테스트 코드가 간결해지고 쉬워졌다
  • private field 나 메소드 접근이 가능하다
  • 테스트 실패 결과가 좀 더 원인파악에 도움이 된다