IT 연구소

단위테스트 프레임워크(Junit5, Spock)

ron_nie 2024. 3. 5. 20:03
728x90

Junit5

어노테이션

@Test

  • 테스트 메서드임을 선언함

@ParameterizedTest

  • 파라미터를 받을 수 있는 테스트를 작성할 수 있음

@RepeatedTest

  • 반복되는 테스트를 작성할 수 있음

@DisplayName

  • 테스트 메소드의 이름을 직접 정의할 수 있음

@BeforeEach

  • 각 테스트 실행 전에 실행됨

@AfterEach

  • 각테스트 실행 후에 실행됨

@Disabled

  • 테스트를 무시함

기본 테스트 작성

더하기 기능 테스트 하기

package com.example.bootstudy.java;

import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.*;

class CalculatorTest {
    @Test // 이거 테스트 메소드야!
    void sum() {
        //given
        Calculator calculator = new Calculator();
        int a = 10;
        int b = 5;

        int expected = 15;

        //when
        int actual = calculator.sum(a, b);

        //then
        assertEquals(expected, actual);
    }
}

동일한 메소드를 다른 파라미터로 테스트하기

package com.example.bootstudy.java;

import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;

import java.util.stream.Stream;

import static org.junit.jupiter.api.Assertions.*;

class CalculatorTest {
    @ParameterizedTest // 특정 파라미터로 테스트하기
    @MethodSource("numberProvider") // 메서드 이름 적어주기
    void sum(int a, int b, int expected) {
        //given
        Calculator calculator = new Calculator();

        //when
        int actual = calculator.sum(a, b);

        //then
        assertEquals(expected, actual);
    }

    static Stream<Arguments> numberProvider() {
        return Stream.of(
                Arguments.arguments(10, 5, 15), // 여기에 원하는 인자를 넣어준다
                Arguments.arguments(-10, -20, -30), // 똑같은 메서드로 인자만 다르게 여러 메서드를 동시에 테스트 할 수 있다.
                Arguments.arguments(0, 0, 0),
                Arguments.arguments(10, -20, -10)
        );
    }
}

예외 테스트하기

package com.example.bootstudy.java;

public class Calculator {
    public int sum(int a, int b) {
        if (a <= 0 || b <= 0) {
            throw new RuntimeException();
        }

        return a + b;
    }
}
    @ParameterizedTest
    @MethodSource("numberProviderForException")
    void sumExceptionTest(int a, int b) { // 예외가 잘 나는지 안나는지만 보면 되기 때문에 expected 값이 필요없다.
        //given
        Calculator calculator = new Calculator();

        //when then
        assertThrows(
                RuntimeException.class,
                () -> calculator.sum(a, b)
        );
    }

    static Stream<Arguments> numberProviderForException() {
        return Stream.of(
                Arguments.arguments(-10, 5),
                Arguments.arguments(1, -100)
        );
    }

BeforeEach, AfterEach, BeforeAll, AfterAll

-저번에 작성했기 때문에 생략함-

Spock

Spock란?

Groovy 언어 기반으로 테스트를 작성할 수 있는 프레임워크이다. Jnit에 비해서 좀 더 짧은 코드를 작성할 수 있고 사용자에 따라 가독성에 더 뛰어나다고 생각할 수 있다. 하지만 이 프레임워크는 Groovy라는 언어를 다시 배워야하기 때문에 러닝커브가 우려될 수 있다.

세팅법

의존성 추가

플러그인에 꼭 추가해줘야한다!

plugins {
	id 'groovy'
}

testImplementation('org.spockframework:spock-core:2.3-groovy-4.0')

groovy 디렉토리 생성

test → New → Directory

Spock의 문법

Spock에서는 given, when, then과 같은 코드를 하나의 block이라고 한다. 각 block 내에는 테스트를 위한 메소드가 최소 하나 존재해야한다.

아래는 블록의 예시들이다.

setup: / given:

  • 테스트에 필요한 환경을 설정하는 작업
  • 다른 블록보다 상위에 위치해야함

when:

  • 테스트 코드를 실행

then:

  • 결과 검증

expect:

  • 테스트 할 코드를 실행 및 검증 (when + then)

where:

  • 반복

테스트 해보기

import com.example.bootstudy.java.Calculator
import spock.lang.Specification

class CalculatorTest extends Specification {
    def "sum"() {
        given:
        def a = 10
        def b = 20
        def expected = 30
        def calculator = new Calculator()

        when:
        def actual = calculator.sum(a, b)

        then:
        actual == expected
    }
}

where

Junit의 ParameterizedTest 어노테이션과 비슷한 기능을 수행한다. 하지만 훨씬 더 직관적이고 사용하기 편하다.

동시에 여러개의 테스트를 수행할 수 있다.

그 와중에 테스트가 실패되었었는데 어디서 어떻게 실패됐는지 잘 알려줘서 편했다. 나는 마지막 테스트에서 100 1000 1100인데 100 100 100이라고 적어서 실패했다.

import com.example.bootstudy.java.Calculator
import spock.lang.Specification

class CalculatorTest extends Specification {
    def "sum"() {
        given:
        def calculator = new Calculator()

        when:
        def actual = calculator.sum(a, b)

        then:
        actual == expected

        where:
        a   | b   | expected
        10  | 20  | 30
        -1  | 1   | 0
        1   | -1  | 0
        0   | 0   | 0
        100 | 1000 | 1100
    }
}

예외테스트

    def "expection"() {
        given:
        def a = -1
        def b = 10
        def calculator = new Calculator()

        when:
        def actual = calculator.sum(a, b)

        then:
        thrown(RuntimeException.class)
    }
반응형