본문 바로가기
개발/Test

[Test] JUnit5을 사용해보자 (1)

by solchan98 2022. 2. 10.

Test Environment
IDE: IntelliJ
Java version: 11
Test Framwork: JUnit 5
Gradle

테스트 코드의 필요성을 느끼게 되다.

사실 YAPP19th활동을 하지 않았으면 아직까지도 테스트 코드 작성의 필요성을 느끼지 못하였을 것이다.

그럼 왜 테스트 코드의 중요성을 느꼈나?

자바를 배우고, 스프링 프레임워크를 공부하면서 어플리케이션 로직을 최적으로 짜는 것이 중요하다고 생각했다.
당시에는 테스트 코드의 존재 또한 모르는 상태였다. 로직 작성을 끝마친 후에는 항상 "드디어! 깔끔하게 마무리 했다!" 이렇게 일이 끝났다고 생각을 했다. 하지만 테스트 코드의 존재를 알게된 후, 또 다른 일이 생겨버린 느낌이었다. 😮‍💨

그런데 YAPP 활동을 하며 프로젝트를 진행하였고, 프로젝트를 진행하면서 테스트 코드의 진짜 필요성을 느꼈다.
프로젝트의 규모가 이전에 공부하면서 진행하던 것에 비해 매우 크다보니 기능 또한 많은 상태였다. 기능이 많다보니 이전에 개발이 끝난 로직이 조금 관련있는 다른 기능을 개발하면서 정상작동을 하지 않는 문제가 발생했다.
테스트 코드가 없었더라면, 위 문제를 최종 기능 테스트를 하기 전에는 발견하지 못 했을 것이다.

프로젝트 진행 전까지 나는 테스트 코드의 필요성을 모를 때 였다. 그런데 우리 팀 백엔드 파트의 파트너 수한님이 테스트 코드는 무조건 필요하다고 짜야한다고 하셔서 시작하게 되었다. 항상 배워가는게 많아서 정말 감사하다.👍

11/28/21 추가 인용
테스트 케이스를 작성하지 않더라도 우리는 개발을 하면서 Postman등의 HTTP클라이언트 도구 혹은 콘솔 등을 이용해 테스트를 진행한다.
하지만 이런 테스트는 재사용이 불가능한 테스트이며, 단계적인 검증이 아닌 산발적인 테스트이다.
By 욜욜욜

JUnit 5

JUnit 5는 자바 기반 어플리케이션 로직을 테스트하는 프레임워크이다.
자바 개발자 93%가 사용한다고 한다. JetBrains 리서치 결과

JUnit를 대체하는 다른 프레임워크는 TestNG, Spock 등이 있다고 한다.
많이 쓰는게 보통 레퍼런스도 많기 때문에 JUnit 5를 공부하기로 결정하였다.

구성

JUnit 5는 크게 아래 그림처럼 구성된다고 볼 수 있습니다.

JUnit Platform

JUnit Platform은 테스트를 실행할 수 있는 런처인 TestEngine API를 제공한다.
예로 스프링부트 어플리케이션을 실행하려면 @SpringBootApplication이 붙은 클래스의 메서드를 실행해야 하고, 일반적인 자바 어플리케이션 또한 main메서드를 실행해야 한다. 하지만 테스트 코드는 위 방식처럼 실행하지 않기 때문에 테스트 코드가 IDE(IntelliJ, Eclipse..)에서 단독적으로 실행될 수 있도록 제공한다.

Jupiter

Jupiter는 위에서 설명한 TestEngin API 구현체로 JUnit 5를 제공한다.

Vintage

JUnit5 이전에 JUnit4, JUnit3가 사용되었다. JUnit4와 JUnit3를 지원하는 TestEngin API 구현체이다. 따라서 JUnit4와 JUnit3를 사용한다면 Vintage를 받아 사용하면 된다.

시작하기

gradle로 java 프로젝트를 생성하면 main과 test패키지가 나뉘어 생성된다.
아래 이미지는 main패키지에 Main클래스를 생성한 후, Test패키지에 해당 Main에 대한 Test클래스를 작성한 것이다(작성은 바로 다음 Test 목차에서 진행).

JUnit5는 기본적으로 다음 6가지 어노테이션을 사용한다.

Test

@Test를 작성하려는 테스트 메서드에 적용하면 해당 메서드가 테스트로 진행된다.
@Test 어노테이션의 import를 보면 junit.jupiter를 확인할 수 있다.
JUnit 5부터 접근 제어자를 사용하지 않아도 된다.
이게 리플렉션을 통해 가능하다고 백기선님이 말씀하셨다. 리플렉션에 대해 추후 공부해야겠다.

import org.junit.jupiter.api.Test;

class Main {

    @Test
    void mainTest() {
        System.out.println("Test Success");
    }

}

테스트 코드르 작성했으면 실행해봐야 한다.

IntelliJ에서는 크게 두 방식으로 테스트를 실행할 수 있다.

  1. 사진의 1번 버튼을 누르면 작성된 모든 테스트 메서드를 수행한다.
  2. 2번 버튼은 해당 메서드만 수행한다.

테스트를 수행하면 다음과 같이 성공했다는 녹색 체크표시와 테스트 결과가 로그에 남겨진다.

이렇게 메인 메서드를 실행하는 것이 아니라 테스트 코드를 단독으로 작동시키는 테스트를 진행해보았다.
현재 작성한 코드는 단순히 출력코드이기 때문에 실패의 경우가 없었다.
추가적인 어노테이션에 대해 배우고 테스트 시나리오를 만들어 진행해보자.

BeforeAll, AfterAll

@BeforeAll, @AfterAll은 이름에서도 느껴지듯이 모든 테스트 코드를 수행하기 전, 모든 테스트 코드를 수행한 후 실행되는 메서드에 붙여지게 된다.
BeforeAll과 AtferAll 어노테이션을 붙이는 메서드는 static으로 선언해줘야 한다.

수행 테스트는 나머지 어노테이션을 모두 알아본 후, 한 번에 테스트 해본다.

BeforeEach, AfterEach

@BeforeEach, @AtferEach는 각각의 테스트 메서드가 실행되기 전 실행되고, 각각의 테스트 메서드가 실행된 후 실행되는 메서드에 붙여지게 된다.

즉, 모든 테스트 메서드가 실행되기 전 후에 매 번 실행되는 것이다.

Disable

@Disable은 내가 작성한 모든 테스트 메서드를 테스트 할 때, 중간에 이 테스트 메서드는 진행하고 싶지 않을 경우 붙이게 된다.

즉, 수행하기 싫은 테스트 메서드에 붙이는 것이다.
아마 테스트 코드가 실패가 뜨는데 당장 고칠 필요가 없거나, 일단 넘기고 싶은 경우에 사용되는 것 같다.
별로 좋지 않은 방법이라고 백기선님이 말하셨다.

6가지 어노테이션 사용 예시

우선 위 6가지 어노테이션을 테스트할 수 있도록 동일한 테스트 메서드 2개와 BeforeAll, AfterAll, BeforeEach, AfterEach 메서드를 각각 생성한다.

import org.junit.jupiter.api.*;

class Main {

    @BeforeAll
    static void beforeAll() {
        System.out.println("Before All\n");
    }

    @AfterAll
    static void afterAll(){
        System.out.println("After All\n");
    }

    @BeforeEach
    void beforeEach() {
        System.out.println("Before Each");
    }

    @AfterEach
    void afterEach() {
        System.out.println("After Each\n");
    }

    @Test
    void test1() {
        System.out.println("Test - 1 Success");
    }

    @Test
    void test2() {
        System.out.println("Test - 2 Success");
    }

}

테스트 실행방법 1번을 통해 전체를 실행하면 결과는 다음과 같이 나온다.

예상한대로 BeforeAll은 모든 테스트가 실행되기 전 한 번 실행되었고, AfterAll 역시 모든 테스트가 종료된 후 한 번만 실행되었다.

BeforeEach와 AfterEach는 두 개의 테스트가 실행되기 전, 후로 매 번 실행된 것을 확인수 있다.

테스트 메서드명 커스터마이징

테스트 수행 결과 리스트를 보면 작성된 테스트명으로 보여진다.
여기서 보여지는 테스트 메서드명을 커스터 마이징 할 수 있다.

@DisplayNameGeneration과 @DisplayName 두 가지 방식이 있다.
첫 번째 방식은 미리 정의해 둔 방식으로 메서드 명을 바꿔주는 방식인데 나는 사용하지 않을 것 같다.

@DisplayNameGeneration

테스트 클래스와 메서드에 모두 적용이 가능하다.
사용방식은 다음과 같다.
"Displayname.Generator.ReplaceUnderScores.class"는 aaa_bbb방식으로 변경해주며, 미리 정의된 다른 녀석을 선택해서 사용할 수 있다.

@DisplayNameGeneration(Displayname.Generator.ReplaceUnderScores.class)
class main() {
    @Test
    void test1() {
        System.out.println("Test - 1 Success");
    }
}

@DisplayName

나는 이 방식을 자주 사용할 것 같다.
사용법도 1번 처럼 매우 간단하다.
참고로 이모지도 가능하다 😲

import org.junit.jupiter.api.*;

@DisplayName("메인 테스트임돵")
class Main {
    @Test
    @DisplayName("테스트 1번 \uD83D\uDE32")
    void test1() {
        System.out.println("Test - 1 Success");
    }

    @Test
    @DisplayName("테스트 2번 \uD83D\uDE32")
    void test2() {
        System.out.println("Test - 2 Success");
    }
}

테스트 메서드 결과 리스트를 보면!
결과에서 보여지는 테스트 메서드명이 커스터마이징된 것을 확인할 수 있다!

'개발 > Test' 카테고리의 다른 글

[Test] Mockito를 사용해보자 (2)  (0) 2022.02.10
[Test] Mockito를 사용해보자 (1)  (0) 2022.02.10
[Test] JUnit5을 사용해보자 (2)  (0) 2022.02.10