반갑습니다!

[JUnit 5] JUnit 5 - 2 본문

개발

[JUnit 5] JUnit 5 - 2

김덜덜이 2020. 12. 28. 00:45

테스트 이름 표기하기

테스트를 실행시키면 메소드 이름으로 테스트가 진행되는 것을 볼 수 있다. 때문에 메소드 이름이 길어질수록 가독성이 떨어진다는 단점이 있다. 이를 보완하기 위해서 JUnit 5에서는 테스트의 이름을 표시할 수 있는 기능을 제공한다.

Java에서는 메소드 이름을 Camel 표기법으로 작성한다. 하지만 이는 가독성 측면에서 봤을 때 Snake 표기법보다 떨어진다. 이 때문에 테스트 코드를 작성할 때는 Snake 표기법으로 작성한다.

@DisplayNameGeneration

이 Annotaion은 DisplayNameGenerator 클래스에 구현된 몇 가지 옵션을 통해 테스트의 가독성을 올려준다.

  • Standard : 기본 JUnit 5의 이름 생성과 동일
  • ReplaceUnderscores : _를 공백으로 변경한다.

이번 포스팅에서는 DisplayNameGenerator.ReplaceUnderscores로 진행해보겠다.

import org.junit.jupiter.api.DisplayNameGeneration;
import org.junit.jupiter.api.DisplayNameGenerator;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;

@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)
class CalculatorTest {
    @Test
    void create_calculator_test() {
        System.out.println("create calculator test");
        Calculator calculator = new Calculator();
        assertNotNull(calculator);
    }

    @Test
    void add_test() {
        System.out.println("add function test");
        Calculator calculator = new Calculator();
        assertEquals(4, calculator.add(2, 2));
    }
}

실행 결과

왼쪽의 테스트 결과 창에 메소드 이름에 포함된 _ 가 모두 공백으로 변경된 것을 확인할 수 있다.

@DisplayName

@DisplayNameGenerator에 대해 학습했지만, 이것만으로 테스트 코드의 가독성이 올라갔다고 보기는 어렵다. 그래서 JUnit 5에서는 더 강력한 기능을 제공한다. @DisplayName Annotaion 선언을 통해 원하는 이름으로 테스트를 확인할 수 있다.

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;

@DisplayName("계산기 테스트")
class CalculatorTest {

    @DisplayName("계산기 객체 생성 테스트")
    @Test
    void create_calculator_test() {
        System.out.println("create calculator test");
        Calculator calculator = new Calculator();
        assertNotNull(calculator);
    }

    @DisplayName("덧샘 테스트")
    @Test
    void add_test() {
        System.out.println("add function test");
        Calculator calculator = new Calculator();
        assertEquals(4, calculator.add(2, 2));
    }
}

실행 결과

테스트 이름이 @DisplayName에서 지정한대로 출력됨을 확인할 수 있다.

참고로 @DisplayNameGeneration 보다 우선 순위가 높다.

Assertion

@Test Annotaion에 대해 학습하면서 assertNotNull() 메소드를 사용했었다. 이번엔 좀 더 다양한 메소드들을 알아보자.

앞서 언급했듯 Assertions 클래스에는 다양한 메소드가 구현되어있다.

메소드 이름이 직관적이라 사용하는데 큰 어려움은 없을 것이다.

org.junit.jupiter.api.Assertions.*

Method Name Description
assertEquals(expected, actual) 실제 값이 기대한 값과 같은지 확인
assertNotNull(actual) 값이 null이 아닌지 확인
assertAll(executables...) 다음 조건이 true인지 확인
assertThrows(exptectedType, executable) 예외 발생 확인
assertTimeout(duration, executable) 특정 시간 안에 실행이 완료되는지 확인

해당 메소드들을 사용해 테스트를 진행하면 된다.

assertAll()

하나의 테스트 코드에서 여러 조건을 테스트할 수 있다. 하지만 이럴 경우 먼저 실행되는 테스트가 실패하게 되면 그 뒤에 테스트는 실행되지 않는다. 이러한 문제는 assertAll()를 통해 해결할 수 있다.

우선 테스트가 실패하도록 해보자.

import org.junit.jupiter.api.Test;

class CalculatorTest {
    @Test
    void create_calculator_test() {
        Calculator calculator = new Calculator();
        assertNull(calculator);
        System.out.println("create calculator test");
    }
}

실행 결과

assertNull()에서 테스트가 종료되어 그 다음 줄이 실행되지 않는다.

이번에는 assertAll() 를 사용해 테스트 코드를 작성해보자. assertAll() 메소드는 Executable 객체를 인자로 받아서 실행한다. 따라서 Executable 객체를 생성해서 인자로 전달해주어도 되고, 익명 객체를 생성해줘도 된다. 하지만 아래의 코드에서는 람다식으로 구현했음을 유의하자.

import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertAll;
import static org.junit.jupiter.api.Assertions.assertNull;

class CalculatorTest {
    @Test
    void create_calculator_test() {
        Calculator calculator = new Calculator();
        assertAll(
                () -> assertNull(calculator),
                () -> System.out.println("create calculator test")
        );
    }
}

실행 결과

테스트가 중간에 실패했지만, 그 이후로도 코드가 진행되었다.

테스트 실패 메시지

테스트가 실패했을 때, 지정한 메시지를 출력해준다면 이에 대한 처리가 훨씬 더 수월할 것이다. 이를 위해 assert 메소드들에 실패 메시지를 추가할 수 있다.

import org.junit.jupiter.api.Test;

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

class CalculatorTest {
    @Test
    void create_calculator_test() {
        Calculator calculator = null;
        assertNotNull(calculator, "Calculator 객체는 null이면 안된다.");
    }
}

실행 결과

미리 작성해 둔 실패 메시지가 같이 출력된다.

테스트 실패

실패 메시지를 작성할 때 "Test " + number + " is fail" 처럼 String 연산을 통해 메시지가 작성될 수 있다. 이런 경우 String 연산을 수행하는데 비용이 든다. 만약 테스트가 항상 성공하는데, 실패 메시지를 위해서 String 연산을 항상 수행한다면 이는 낭비가 될 것이다. 따라서 assert 메소드들은 이를 방지할 수 있도록 Supplier<String> 객체를 통해 실패 메시지를 전달받을 수 있도록 구현되어 있다.

import org.junit.jupiter.api.Test;

import java.util.function.Supplier;

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

class CalculatorTest {
    @Test
    void create_calculator_test() {
        Calculator calculator = null;
        // 익명 객체 사용
        assertNotNull(calculator, new Supplier<String>() {
            @Override
            public String get() {
                return "Calculator 객체는 null이면 안된다.";
            }
        });
        // 람다식 사용
        assertNotNull(calculator, () -> "Calculator 객체는 null이면 안된다.");
    }
}

위의 코드처럼 익명 객체 또는 람다식을 통해 실패 메시지를 작성하면, 테스트가 실패한 경우에만 String 연산을 수행해 연산 수행 비용을 절약할 수 있다.