Spock 이 뭐지?
응 아냐
Spock 이란 BDD(Behaviour-Driven Development) 프레임워크이다. TDD(Test-driven Development) 프레임워크인 JUnit 보다 기대하는 동작과 테스트의 의도를 더 잘 드러내 준다고 한다.
뿐만 아니라 설정과 사용법도 쉽다!
어차피 블로그에서 짜집기 하는거 요점만 써야겠다.
JUnit 도 잘 모르지만 JUnit 과의 차이를 들어서 어떤 점에서 테스트의 의도가 더 잘 보이는지 알아보자.
여러 글에서도 다설명하고 있지만, 나도 똑같이 쓴다.
설정
build.gradle
의존성을 넣어준다.
plugins {
id 'org.springframework.boot' version '2.2.4.RELEASE'
id 'io.spring.dependency-management' version '1.0.9.RELEASE'
id 'java'
id 'groovy' // groovy 지원
}
group 'org.example'
version '1.0-SNAPSHOT'
sourceCompatibility = 13
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.spockframework:spock-core:1.1-groovy-2.4'
testImplementation 'org.spockframework:spock-spring:1.1-groovy-2.4'
}
Test 코드 작성
JUnit 과 동일하게 Test 코드로 잡아준 경로에 .groovy
파일을 만들어준다.
어? 난 그루비 모르는데? -> 괜찮다.
아래와 같이 class 를 만들어 준다. spock.lang.Specification
class 를 상속 받는것이 중요하다.
import spock.lang.Specification
class SpockTest extends Specification {
}
테스트를 위한 더미 비지니스
간단하게 enum
하나만 만들었다.
가격을 지불하는 대상의 등급에 따라 지불 가격을 계산해주는 enum
public enum FeeCalculateType {
/*일반 등급은 가격을 깍지 않음 */
NORMAL("NORMAL", (amount) -> {
validateAmount(amount); //공통 금액 체크
return calculate(amount, 0);
}),
/* VIP 등급은 100원 깍아줌 */
VIP("VIP", (amount) -> {
validateAmount(amount); //공통 금액 체크
return calculate(amount, -100);
})
;
String desc;
Function<Long, Long> expression;
FeeCalculateType(String desc, Function<Long, Long> expression) {
this.desc = desc;
this.expression = expression;
}
static boolean validateAmount(long amount) {
if(amount < 0)
throw new IllegalArgumentException("not accept negative amount -> " + amount);
return true;
}
static long calculate(long amount, long addr) {
if(amount+addr <= 0) return 0;
else return amount + addr;
}
public long calculate(long amount) { return expression.apply(amount); }
}
다됐다. 이제 테스트 함수를 만들어서 비교해보자.
일반등급 테스트 1 - 단순
먼저 JUnit 으로 짠 테스트 코드
@Test
public void 일반인은_안깍아준다() {
//given
Long 지불금액 = -1L;
Long 계산금액 = -1L;
지불금액 = 1000L;
//when
계산금액 = FeeCalculateType.NORMAL.calculate(지불금액);
//expect
assertEquals(지불금액, 계산금액);
}
그리고 Spock 테스트 코드
def "일반인은 안깍아준다"() {
given:
Long 지불금액 = 1000
when:
Long 계산금액 = FeeCalculateType.NORMAL.calculate(지불금액)
then:
지불금액 == 계산금액
}
그렇다. groovy
에서는 함수 정의(정확히는 동적 타입 선언)를 def
요딴식으로 한다. 그리고 함수명으로 String 을 받기 때문에 JUnit Test 메소드의 java 함수명의 제약에서 벗어날 수 있다.
한 케이스만 봤을 때는 뭐가 더 좋은지 느낌이 안 올 수도 있다.
일반등급 테스트 2 - 복수의 파라미터
Junit
@Test
public void 일반인은_아무리_많이써도_안돼() {
Long 지불금액 = -1L;
Long 계산금액 = -1L;
지불금액 = 1000L;
계산금액 = FeeCalculateType.NORMAL.calculate(지불금액);
assertEquals(지불금액, 계산금액);
지불금액 = 5000L;
계산금액 = FeeCalculateType.NORMAL.calculate(지불금액);
assertEquals(지불금액, 계산금액);
지불금액 = 10000L;
계산금액 = FeeCalculateType.NORMAL.calculate(지불금액);
assertEquals(지불금액, 계산금액);
지불금액 = 50000L;
계산금액 = FeeCalculateType.NORMAL.calculate(지불금액);
assertEquals(지불금액, 계산금액);
지불금액 = 100000L;
계산금액 = FeeCalculateType.NORMAL.calculate(지불금액);
assertEquals(지불금액, 계산금액);
}
Spock
def "일반인은 아무리 많이써도 안돼"() {
expect:
계산금액 == FeeCalculateType.NORMAL.calculate(지불금액)
where:
지불금액 | 계산금액
1000 | 1000
5000 | 5000
10000 | 10000
50000 | 50000
100000 | 100000
100000 | 100000
}
아 이것만봐도 느낌이 온다. 테스트 조건과 행위를 나눌 수 있다.
일반 등급외에 VIP 등급 테스트 또한 마찬가지기 때문에 자세한 코드는 생략한다.
메소드의 호출횟수 체크
이런것도 할 수 있어?
테스트를 위한 억지 비지니스
억지 알람 컨트롤러
public class DummyController {
private DummyService dummyService;
public DummyController(DummyService dummyService) {
this.dummyService = dummyService;
}
public void sendMessage(String message, int sendCount) {
for(int i=0; i<sendCount; i++) {
dummyService.sendToSms(message);
dummyService.sendToKaKao(message);
}
}
}
억지 알람 서비스
public class DummyService {
public void sendToKaKao(String message) { System.out.println("send to kakao -> " + message); }
public void sendToSms(String message) { System.out.println("send to sms -> " + message); }
}
호출횟수를 테스트해보자
class ServiceSpockTest extends Specification {
def "호출 횟수 확인"() {
given:
def mockDummyService = Mock(DummyService)
DummyController dummyController = new DummyController(mockDummyService)
String message = "알람받아라~"
int sendCount = 12;
when:
dummyController.sendMessage(message, sendCount)
then:
(12.._) * mockDummyService.sendToKaKao(message)
}
}
then: (12..)
는 호출 횟수가 12번 이상인지를 체크하는 문법이다.
직관적이고 짱 쉬운 것 같다.
끝.
참고