-
Spock 로 테스트 해보기🌱 spring 2022. 10. 6. 13:53
Spock ❓❓❓
Spock 는 BDD(Behaviour-Driven Development) 프레임워크 이다.
- Java 및 Groovy를 위한 테스트 프레임워크
- 내부적으로 JUnit Runner로 구동되며, 대부분의 IDE에서 지원된다.
TDD 프레임워크인 JUnit 과 비슷한 점이 많지만, 기대하는 동작과 테스트의 의도를 더 명확하게 드러내주고 산만한 코드는 뒤로 숨겨주는 등 큰 장점이 있다.
기존 JUnit의 단점
- 테스트시 중복 코드가 많다.
- JUnit, Hamcrest, Mockito를 알아야 코드를 이해할 수 있다.
Spock 시작하기
- spring-boot : 2.7.4
- java : 11
build.gradle
testImplementation'org.spockframework:spock-core:2.3-groovy-4.0' testImplementation'org.spockframework:spock-spring:2.3-groovy-4.0'
이렇게 의존성을 추가해주고
groovy 파일을 생성 후 Specification 클래스를 상속 받으면 spock을 사용할 수 있다.
‘소수점 버림’ 테스트 해보기
CalculateTest.java
public class CalculateTest { public static long calculate(long amount , float rate, RoundingMode roundingMode) { return BigDecimal.valueOf(amount * rate * 0.01) .setScale(0, roundingMode ).longValue(); } }
SpockTest.groovy - 1
class SpockTest extends Specification { def "금액의 퍼센트 계산 결과 값의 소수점 버림을 검증"() { //given RoundingMode 소수점버림 = RoundingMode.DOWN; //when def calculate = CalculateTest.calcuate(10000L, 0.1f, 소수점버림) //then calculate == 10L } }
💣 Error 발생
- 코드 블록을 선언하지 않은 것이 실패 원인
Block (코드 블록)
Spock에서는 given, when, then 과 같은 코드 블록 을 block이라고 부른다.
block은 테스트 메서드 (feature method) 내 최소한 하나는 있어야 한다.
JUnit에서는 있어도 되고 없어도 되는데 Spock에서는 필수이다.
- JUint에서는 주석으로 하지만, spock에서는 주석처리를 하지 않음
given( or setup) JUnit의 Given 처럼 테스트에 필요한 환경을 설정하는 작업
- 항상 다른 블록보다 상위에 위치when 테스트 코드를 실행 then 테스트 코드 결과 검증, 예외 및 조건에 대한 결과 확인 작성한 코드
- 한줄이 assert 문expect 테스트 할 코드 실행 및 검증 (when + then) SpockTest.groovy - 2
class SpockTest extends Specification { def "금액의 퍼센트 계산 결과 값의 소수점 버림을 검증"() { given: RoundingMode 소수점버림 = RoundingMode.DOWN; when: def calculate = CalculateTest.calcuate(10000L, 0.1f, 소수점버림) then: calculate == 10L } }
잘 수행된다!
Where blocks
테스트를 하면서 다양한 케이스를 검증할 때가 있다.
- where block 을 사용하면 간단하게 해결할 수 있다.
def "여러 금액의 퍼센트 계산 결과값의 소수점 버림을 검증"() { given: RoundingMode 소수점버림 = RoundingMode.DOWN; expect: CalculateTest.calcuate(amount, rate, 소수점버림) == result where: amount | rate | result 10000L | 0.1f | 10L 2799L | 0.2f | 5L 159L | 0.15f | 0L 2299L | 0.15f | 3L }
테스트가 실패했을 경우 JUnit은 제일 먼저 실패한 케이스만 알 수 있다.
하지만, Spock는 실패한 모든 테스트 케이스와 그 내용을 더 자세히 알 수 있다.
예외 케이스
0보다 작은 음수가 들어왔을 때 → “예외” 상황
def "음수가 들어오면 예외가 발생하는지 확인"() { given: RoundingMode 소수점버림 = RoundingMode.DOWN; when: CalculateTest.calcuate(-10000L, 0.1f, 소수점버림) then: def e = thrown(NegativeNumberNotAllowException.class) e.message == "음수는 계산할 수 없다." }
Spock에서 예외는 thrown() 메서드로 검증이 가능하다.
- thrown() 메서드는 발생한 예외를 확인할 수 있을 뿐 아니라 객체를 반환해
- 예외에 따른 메시지도 검증을 할 수 있다.
Mock 테스트
Spock에서 Mock 테스트도 간단하다.
def "주문 금액의 소수점 버림을 검증"() { given: RoundingMode 소수점버림 = RoundingMode.DOWN; def orderSheet = Mock(OrderSheet.class) when: long amount = orderSheet.getTotalOrderAmount() then: orderSheet.getTotalOrderAmount() >> 10000L 10L == CalculateTest.calcuate(amount, 0.1f, 소수점버림) }
가짜 객체의 반환 값은 ‘>>’으로 설정할 수 있고 예외를 발생시키고 싶다면 아래와 같이 하면 된다.
orderSheet.getTotalOrderAmount() >>
Bean의 Mocking
Spock 를 쓰더라도 Spring의 Bean을 mocking 하려면 Mockito 방식을 사용해야 한다.
@MockBean OrderRepository orderRepository def "주문 금액 소수점 버림 조회" () { given: given(orderRepository.findById(anyLong())) .willReturn(Optional.of(new Order(10000L, 0.1f))) when: Order order = orderService.getOrder(1) then: order.getPrice() == "10L" }
- 하지만 이 경우 groovy 언어 특성상 Overloading Method를 mocking할 때 어떤 메서드를 Mocking 해야 할지 선택하지 못해 Mocking 실패로 테스트가 실패하게 된다.
그럴 경우 Spock에서 제공하는 DetachedMockFactory 를 통한 Mock 생성을 통해 bean에 대한 mock 생성이 가능하다.
- 하지만, 이 경우에도 JpaRepository인 경우 mocking하지 못한다.
- JpaRepository 인터페이스 mocking이 필요할 경우 JUnit 또는 Spock에서 Mockito를 사용해야 한다.
참고 자료
https://meetup.toast.com/posts/268
'🌱 spring' 카테고리의 다른 글
JPA - N + 1 문제 (0) 2022.10.11 영속성 컨텍스트 (0) 2022.10.11 JPA와 Hibernate, Spring data JPA (0) 2022.10.10 MVCC (1) 2022.10.05 QueryDSL (1) 2022.10.05