본문 바로가기

Programing/테스트

[test] 박*영과 함께하는 spock framework pair programming

제목은 "박*영과 함께하는 spock framework pair programming"이라고 쓰고 사실 꼰대질(?!)을 정리한 것이다.

 

아마도.. TL;DR (too long, don’t read) 가 될 것 같다.

 

박*영 님은 같은 회사의 개발자이다.

기존에 jUnit으로 되어 있던 테스트 코드들을 Groovy 기반의 Spock 프레임워크를 도입해서 적용하고 있다고 한다.

 

처음 접하는 사람들이 공통적으로 겪을 수 있다고 생각해서 공유 및 기록 차원에서 끄적여보았다.

2020-01-14 (화) - 꼰대질1

포인트를 대량 적립해주는 서비스(가칭: MassiveAccumulatingService)에 대한 테스트를 만들고 있었다.

조언

1. 테스트의 이름

 - 테스트 대상의 이름을 사용하는 것보다는 테스트가 수행하는 것에 대해 적는 것이 좋다고 권유했다.

예)

MassiveAccumulatingService sut

def "accumulate 테스트"() {

}

보다는

def "이미 데이터가 적립되어 있으면 InvalidStateException 예외가 발생한다."() {
  // 생략
  then:
    thrown(InvalidStateException.class)
}

2. 테스트를 통한 테스트 대상(sut; system under test)에 대한에 대한 개선

 - 테스트 코드를 만들다 보면 코드의 냄새(smell)가 느껴질 수 있음

 - 예를 들면 SOLID의 원칙중 단일 책임의 원칙이 지켜지지 않았을 때 테스트가 지나치게 복잡하고, 같은 동작을 하는 것이 발생

 - 테스트 코드는 1. 기능(비즈니스 로직)에 대한 테스트의 효과 뿐만 아니라 2. 리팩토링 도 하는 긍정적인 효과가 있다.

2020-01-16 (목) - 꼰대질2

*Test vs *Spec

인텔리제이에서는 command-shift-t 를 누르면 테스트 코드를 자동으로 만들어준다.

기본으로 Test로 끝나는 형태의 이름을 제안한다.

Spock 진영에서는 Specification의 줄임말인 Spec을 postfix로 쓰는 경우가 많다. (결국은 개취)

인텔리제이 설정 방법 좀 알려주세요~

where: 블럭

Spock의 블럭 중 where: 가 등장했다.

1. @Unroll 의 사용

2. placeholder를 통한 테스트 이름 이용

예)

    @Unroll
    def "isEntireCancel : 상품금액(#goodsAmt)과 취소금액(#cancelAmt)이면 #isExpectedEntireCancel"() {
        given:
        def sut = new RoomCancelParams()
        sut.goodsAmt = goodsAmt
        sut.cancelAmt = cancelAmt

        when:
        boolean result = sut.isEntireCancel()

        then:
        result == isExpectedEntireCancel

        where:
        goodsAmt | cancelAmt | isExpectedEntireCancel
        10_000   | 10_000    | true
        10_000   | 9_000     | false
    }

위의 테스트 코드를 실행하면 아래와 같이 좀더 명시적인 테스트 결과가 목록에 리스트업 될 수 있다.

나의 경우 다른 응용 방법으로는 테스트에 직접적으로 필요하지는 않지만 테스트 이름을 받아서 표시하는 것도 종종 사용한다.

    @Unroll
    def "getHeader - #description"(String userAgentWhenCreated, String headerName, String userAgentExpected, String description) {
        given:
        sut = new MockHttpServletRequest(userAgentWhenCreated, "127.0.0.1")

        when:
        String response = sut.getHeader(headerName)

        then:
        response == userAgentExpected

        where:
        userAgentWhenCreated | headerName   | userAgentExpected | description
        "UA"                 | "User-Agent" | "UA"              | "적용된 값을 잘 가져온다."
        "Mozilla/5.0"        | "User-Agent" | "Mozilla/5.0"     | "다른 적용된 값을 잘 가져온다."
        null                 | "User-Agent" | null              | "적용된 값이 없으면 null을 반환한다."
        "UA"                 | "Server"     | null              | "적용된 값이 있지만 다른 header를 가져오면 null을 반환한다."
    }

2020-01-17 리스폰스 & 리스펙트

*영 님이 공유해주신 내용: IntelliJ IDEA에서 기본 테스트 클래스 접두어 바꾸기

기본 Test로 되어 있는데 Spock 테스트 코드는 Specification의 줄임말인 Spec으로 하는 경우가 있다.

테스트 대상이 MassiveAccumulatingService 라면 MassiveAccumulatingServiceSpec 처럼 말이다.

참고: https://stackoverflow.com/questions/35247586/change-default-test-class-name-suffix-in-intellij-idea

*영 님께 리스펙트! (역시 청출어람)