저작권 안내: 저작권자표시 Yes 상업적이용 No 컨텐츠변경 No

스프링5 입문

JSP 2.3

JPA 입문

DDD Start

인프런 객체 지향 입문 강의

JUnit 5 소개

TDD 또는 Test 2017.10.10 21:24

[갱신]

2018-11-19: junit 5.3.1과 maven-surefire-plugin 2.22.0 버전에 대한 내용 추가


Junit 5 정식 버전이 나왔다. 테스트 코드를 작성하는 개발자 입장에서 JUnit 5를 보면 JUnit 4에 비해 중요한 차이점은 다음과 같다.

  • JUnit 4가 단일 jar였던 것에 비해, JUnit 5는 크게 JUnit Platform, JUnit Jupiter, JUnit Vintage 모듈로 구성되어 있다.
  • 테스트 작성자를 위한 API 모듈과 테스트 실행을 위한 API가 분리되어 있다.
    • 예를 들어, JUnit Jupiter는 테스트 코드 작성에 필요한 junit-jupiter-api 모듈과 테스트 실행을 위한 junit-jupiter-engine 모듈로 분리되어 있다.
  • 자바 8 또는 그 이상 버전을 요구한다.

모듈 개요

JUnit 5를 사용해서 테스트를 작성하고 실행하려면 모듈의 개략적인 구성 정도는 알아야 설정을 이해하는데 도움이 된다. 모듈은 크게 다음과 같은 구조를 갖는다. (세부적인 모듈 목록은 JUnit 5 User Guide에서 확인할 수 있다.)



JUnit Platform은 테스트를 발견하고 테스트 계획을 생성하는 TestEngine 인터페이스를 정의하고 있다. Platform은 TestEngine을 통해서 테스트를 발견하고, 실행하고, 결과를 보고한다.


TestEngine의 실제 구현체는 별도 모듈로 존재한다. 이 모듈 중 하나가 jupiter-engine이다. 이 모듈은 jupiter-api를 사용해서 작성한 테스트 코드를 발견하고 실행한다. Jupiter API는 JUnit 5에 새롭게 추가된 테스트 코드용 API로서, 개발자는 Jupiter API를 사용해서 테스트 코드를 작성할 수 있다.


기존에 JUnit 4 버전으로 작성한 테스트 코드를 실행할 때에는 vintage-engine 모듈을 사용한다.


만약 테스트 코드를 작성하기 위한 새로운 API를 창안하다면, 그 API에 알맞은 엔진 모듈을 함께 구현해서 제공하면 JUnit Platform 수정없이 새로 창안한 테스트 API를 실행하고 결과를 리포팅할 수 있게 된다.


프로젝트 의존 설정


[노트]

JUnit 버전별 프로젝트 의존 설정은 https://junit.org/junit5/docs/버전/user-guide/ 문서를 참고한다.


메이븐 프로젝트 설정(JUnit 5.3 + maven-surefire-plugin 2.22.0)


메이븐 프로젝트를 사용한다면 다음과 같이 설정한다.


<?xml version="1.0" encoding="UTF-8"?>

<project ...생략>

    ...생략


    <dependencies>

        <dependency>

            <groupId>org.junit.jupiter</groupId>

            <artifactId>junit-jupiter-api</artifactId>

            <version>5.3.1</version>

            <scope>test</scope>

        </dependency>


        <dependency>

            <groupId>org.junit.jupiter</groupId>

            <artifactId>junit-jupiter-engine</artifactId>

            <version>5.0.1</version>

            <scope>test</scope>

        </dependency>

    </dependencies>


    <build>

        <plugins>

            <plugin>

                <artifactId>maven-compiler-plugin</artifactId>

                <version>3.1</version>

                <configuration>

                    <source>1.8</source>

                    <target>1.8</target>

                    <encoding>utf-8</encoding>

                </configuration>

            </plugin>


            <plugin>

                <artifactId>maven-surefire-plugin</artifactId>

                <version>2.22.0</version>

            </plugin>

        </plugins>

    </build>


</project>


의존 설정:

  • org.junit.jupiter:junit-jupiter-api :테스트 코드 작성에 필요한 API 제공

maven-surefire-plugin 설정

  • 2.22.0 이상 버전 사용: 2.22.0 버전부터 JUnit Platform에 대한 네이티브 지원
이렇게 설정하면 mvn test 명령어를 사용해서 Jupiter API를 사용해서 작성한 테스트를 메이븐에서 실행할 수 있다.


메이븐 프로젝트 설정(maven-surefire-plugin 2.19.0 기준)


maven-surefire-plugin 버전을 2.22.0 이전 버전을 사용해야 한다면 다음과 같이 2.19.0 버전을 사용하고 junit-platform-surefire-provider를 추가한다.


<?xml version="1.0" encoding="UTF-8"?>

<project ...생략>

    ...생략


    <dependencies>

        <dependency>

            <groupId>org.junit.jupiter</groupId>

            <artifactId>junit-jupiter-api</artifactId>

            <version>5.0.3</version>

            <scope>test</scope>

        </dependency>


    </dependencies>


    <build>

        <plugins>

            <plugin>

                <artifactId>maven-compiler-plugin</artifactId>

                <version>3.1</version>

                <configuration>

                    <source>1.8</source>

                    <target>1.8</target>

                    <encoding>utf-8</encoding>

                </configuration>

            </plugin>


            <plugin>

                <artifactId>maven-surefire-plugin</artifactId>

                <version>2.19.1</version>

                <dependencies>

                    <dependency>

                        <groupId>org.junit.platform</groupId>

                        <artifactId>junit-platform-surefire-provider</artifactId>

                        <version>1.0.3</version>

                    </dependency>

                    <dependency>

                        <groupId>org.junit.jupiter</groupId>

                        <artifactId>junit-jupiter-engine</artifactId>

                        <version>5.0.3</version>

                    </dependency>

                </dependencies>

                -->

            </plugin>

        </plugins>

    </build>


</project>


의존 설정:

  • org.junit.jupiter:junit-jupiter-api :테스트 코드 작성에 필요한 API 제공
  • org.apiguardian:apiguardian-api: 컴파일 경고 메시지를 제거하고 싶으면 추가 

maven-surefire-plugin 설정

  • 2.19.1 버전 사용: 2.20이 메모리릭 문제가 있고, 2.20.1 버전은 JUnit 5.0과 호환되지 않아 mvn test 시 에러 발생함(이슈 참고)
  • JUnit 5의 플랫폼을 사용해서 테스트를 실행하기 위해 플러그인 의존 추가
    • junit-platform-surefire-provider: 메이븐 Surefire에서 JUnit Platform을 실행하기 위한 모듈
    • junit-jupiter-engine: Jupiter API로 작성한 테스트를 위한 엔진 모듈
이렇게 설정하면 mvn test 명령어를 사용해서 Jupiter API를 사용해서 작성한 테스트를 메이븐에서 실행할 수 있다.


그레이들 프로젝트 설정


그레이들 프로젝트에서 JUnit Jupiter 테스트를 사용하기 위한 설정은 아래와 같다.


group 'junit5-prac'

version '1.0-SNAPSHOT'


buildscript {

    repositories {

        mavenCentral()

    }

    dependencies {

        classpath 'org.junit.platform:junit-platform-gradle-plugin:1.0.1'

    }

}


apply plugin: 'org.junit.platform.gradle.plugin'

apply plugin: 'java'


sourceCompatibility = 1.8


repositories {

    mavenCentral()

}


dependencies {

    testCompile("org.junit.jupiter:junit-jupiter-api:5.0.1")

    testRuntime("org.junit.jupiter:junit-jupiter-engine:5.0.1")

}


buildscript로 빌드 과정에서 그레이들이 JUnit Platform을 사용하도록 설정하고, apply plugin으로 플러그인을 적용한다. dependencies에는 Jupiter API를 사용해서 테스트를 작성하기 위한 API와 런타임에 실행하기 위한 엔진 의존을 추가한다. Jupiter Engine은 테스트를 실행할 때 필요하므로 testRuntime으로 설정한다.


이제 gradle test 명령어로 Jupiter API를 사용해서 작성한 테스트를 실행할 수 있다.


Jupiter API: 테스트 작성과 실행


테스트 코드 작성하기


Jupiter API를 이용해서 테스트 코드를 작성하는 방법은 JUnit 4 버전과 크게 다르지 않다. 다음은 Jupiter API를 이용한 테스트 코드 작성예이다.


import org.junit.jupiter.api.Test;


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


public class PasswordMeterTest {


    @Test

    void weak() {

        PasswordMeter meter = new PasswordMeter();

        assertEquals(PasswordLevel.WEAK, meter.meter("1234"));

    }

}


JUnit 4를 이용한 테스트 코드와 거의 동일한 것을 알 수 있다. @Test 애노테이션을 이용해서 테스트로 사용할 메서드를 지정하고, assertEquals() 메서드를 이용해서 기대하는 값과 실제 결과 값이 같은지 비교한다. 차이점은 @Test 애노테이션과 assertEquals() 메서드가 속한 패키지이다. Jupiter API의 패키지는 org.junit.jupiter.api로 시작한다.


인텔리J에서 실행하기


인텔리J 최신 버전은 JUnit 5를 지원한다. 메이븐이나 그레이들 프로젝트를 임포트했다면 해당 테스트 클래스나 메서드를 실행하면 된다.


[참고]

인텔리J 버전마다 내장하고 있는 JUnit 버전이 다르다. 2017.3 버전부터는 프로젝트에서 사용하는 JUnit 버전을 사용한다. 반면에 2017.3 이전의 경우에는 내장하고 있는 특정 버전을 사용한다. 예를 들어 2017.2.5의 경우는 Jupiter 5.0.0 버전을 내장하고 있다.([인텔리j]\plugins\junit\lib에 위치) 2017.3 이전 버전에서 JUnit Platform Launcher와 Jupiter Engine을 내장된 버전 대신 다른 버전을 사용하고 싶다면 User Guide의 Ide 지원을 참고한다.


이클립스에서 실행하기 1: JUnit 4를 이용한 실행 


현재 사용중인 이클립스 Oxygen(4.7) 버전은 아직 JUnit 5의 Platform과 Jupiter API를 지원하지 않는다. 이런 환경에서 Jupiter API로 작성한 테스트를 실행하려면 다음과 같은 의존을 추가해 주어야 한다.


       <!-- JUnit5를 지원하지 않는 환경(eclipse oxygen 등)에서 실행하기 위한 설정 -->

        <dependency>

            <groupId>junit</groupId>

            <artifactId>junit</artifactId>

            <version>4.12</version>

        </dependency>

        <dependency>

            <groupId>org.junit.platform</groupId>

            <artifactId>junit-platform-runner</artifactId>

            <version>1.0.1</version>

            <scope>test</scope>

        </dependency>

        <dependency>

            <groupId>org.junit.jupiter</groupId>

            <artifactId>junit-jupiter-engine</artifactId>

            <version>5.0.1</version>

            <scope>test</scope>

        </dependency>


JUnit 4 버전에 대한 의존을 추가했는데, 그 이유는 JUnit 4의 @RunWith를 사용해서 Jupiter API 기반 실행하기 위함이다. junit-platform-runner 모듈은 Jupiter 기반 테스트를 실행할 수 있는 Runner인 JUnitPlatform을 제공한다. 이 Runner를 사용하면 JUnit 4만 지원하는 개발 환경에서 Jupiter API로 작성한 테스트를 실행할 수 있다. 다음은 사용 예이다. 아래 코드에서 @Test 애노테이션의 JUnit 4의 @Test가 아니라 Jupiter API의 @Test 임을 알 수 있다.


import org.junit.jupiter.api.Test;

import org.junit.platform.runner.JUnitPlatform;

import org.junit.runner.RunWith;


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


@RunWith(JUnitPlatform.class)

public class PasswordMeterTest4Eclipse {


    @Test

    void weak() {

        PasswordMeter meter = new PasswordMeter();

        assertEquals(PasswordLevel.WEAK, meter.meter("1234"));

    }

}



이클립스에서 실행하기 2: 플러그인 사용


Oxygen에서 JUnit 5를 실행할 수 있는 또 다른 방법은 아직은 베타 버전인 플러그인을 설치하는 것이다. 이에 대한 설치 방법은 https://wiki.eclipse.org/JDT_UI/JUnit_5 문서에서 확인할 수 있다.



Jupiter API: assert 메서드


기본 assert 메서드, fail() 메서드


Jupiter API의 org.junit.jupiter.api.Assertions 클래스는 값 검증을 위한 assert로 시작하는 static 메서드를 제공하고 있다. 주요 메서드는 다음과 같다.

  • assertEquals(expected, actual) : int, long 등 기본타입과 Object에 대한 assertEquals 메서드가 존재한다.
  • assertNotEquals(Object unexpected, Object actual)
  • assertTrue(boolean condition)
  • assertFalse(boolean condition)
  • assertNull(Object actual)
  • assertNotNull(Object actual)
  • fail()

assertAll()


아래 코드를 보자.


@Test

void assertEach() {

    Game game = new Game(123);

    Score score = game.guess(134);

    assertEquals(1, score.getStrikes());

    assertEquals(1, score.getBalls());

}


이 코드에서 만약 첫 번째 assertEquals()가 실패하면 그 시점에서 테스트 실패하므로 두 번째 assertEquals()를 실행하지 않는다. 이 두 assertEquals()는 실제로는 하나의 score를 검증하는 것이므로 개념적으로 하나를 검증하는 것이다. 이럴 때 유용하게 사용할 수 있는 것이 assertAll()이다. assertAll()은 여러 검증을 하나로 묶어서 테스트 할 수 있게 해준다. 다음은 assertAll()의 예를 보여주고 있다.


public class AssertAllTest {

    @Test

    void assertAllSample() {

        Game game = new Game(123);

        Score score = game.guess(145);

        assertAll(

                () -> assertEquals(2, score.getStrikes()),

                () -> assertEquals(1, score.getBalls())

        );

    }

}


assertAll()은 함수형인터페이스인 Executable 목록을 파라미터로 갖는다.(Executable 인터페이스는 파라미터가 없고 리턴 타입이 void인 execute() 메서드를 정의하고 있다.) 함수형 인터페이스이므로 위 코드와 같이 람다식을 사용해서 여러개의 검증을 목록으로 전덜할 수 있다.


assertAll()의 특징은 목록으로 받은 모두 Executable을 실행한다는 점이다. 그리고 그 중에서 실패한 검증에 대해서만 리포트를 한다. 예를 들어, 위 코드에서 assertAll()로 전달한 모든 Executable이 검증에 실패했다고 하자. 이를 인텔리J에서 실행해보면 다음과 같이 assertAll()에서 실패한 모든 검증 결과가 콘솔에 출력되는 것을 알 수 있다.


xpected :2

Actual   :3

 <Click to see difference>


Expected :1

Actual   :0

 <Click to see difference>


assertThrows()


실행한 코드에서 특정 익셉션이 발생하는지 확인할 때에는 assertThrows() 메서드를 사용한다. 다음은 사용예이다.


@Test

void simple() {

    assertThrows(ArithmeticException.class, () -> divide(100, 0));

}


private int divide(int op1, int op2) {

    return op1 / op2;

}



Jupiter API: 테스트 라이프사이클


Jupiter API도 JUnit 4와 동일한 라이프사이클을 갖는다. Jupiter API의 애노테이션이 좀 더 의미를 살리는 이름을 사용한다.


public class LifecycleTest {


    @BeforeAll // JUnit 4의 @BeforeClass

    static void initAll() {

        System.out.println("initAll");

    }


    @BeforeEach // JUnit 4의 @Before

    void init() {

        System.out.println("init");

    }


    @Test

    void someTest() {

        System.out.println("someTest");

    }


    @Test

    void anyTest() {

        System.out.println("anyTest");

    }


    @AfterEach // JUnit 4의 @After

    void tearDown() {

        System.out.println("tearDown");

    }


    @AfterAll // JUnit 4의 @AfterClass

    static void tearDownAll() {

        System.out.println("tearDownAll");

    }


}



보조 애노테이션: @DisplayName과 @Disabled


@DisplayName은 테스트 클래스나 메서드의 표시 이름을 지정한다.


@DisplayName("라이프사이클 테스트")

public class LifecycleTest {


    @DisplayName("어떤 테스트")

    @Test

    void someTest() {

        System.out.println("someTest");

    }


    @Disabled("테스트 환경 구성 필요")

    @Test

    void anyTest() {

        System.out.println("anyTest");

    }


위 코드를 실행하면 아래 그림과 같이 테스트 결과에서 @DisplayName에 지정한 이름을 사용하는 것을 알 수 있다.


위 코드에서 @Disabled 애노테이션을 사용했는데, 이 애노테이션은 테스트를 실행 대상에서 제외한다. 위 그에서도 테스트를 하지 않고 생략한 것을 알 수 있다.


다음에는


이어지는 글(http://javacan.tistory.com/464)에서는 계속해서 Jupiter API에 추가된 기능을 추가로 살펴볼 예정이다.








Posted by 최범균 madvirus

댓글을 달아 주세요