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

스프링4 입문

스프링 4

DDD Start

객체 지향과
디자인 패턴

JSP 2.3

JPA 입문

JUnit 5 소개

TDD 또는 Test 2017.10.10 21:24

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를 실행하고 결과를 리포팅할 수 있게 된다.


프로젝트 의존 설정


메이븐 프로젝트 설정


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


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

<project ...생략>

    ...생략


    <dependencies>

        <dependency>

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

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

            <version>5.0.1</version>

            <scope>test</scope>

        </dependency>


        <!-- 컴파일 경고 메시지 제거 목적 -->

        <dependency>

            <groupId>org.apiguardian</groupId>

            <artifactId>apiguardian-api</artifactId>

            <version>1.0.0</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.1</version>

                    </dependency>

                    <dependency>

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

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

                        <version>5.0.1</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 최신 버전(2017.2.5)은 JUnit 5를 지원한다. 메이븐이나 그레이들 프로젝트를 임포트했다면 해당 테스트 클래스나 메서드를 실행하면 된다.


[참고]

인텔리J 버전마다 내장하고 있는 JUnit 버전이 다르다. 2017.2.5의 경우는 Jupiter 5.0.0 버전을 내장하고 있다.([인텔리j]\plugins\junit\lib에 위치) 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

댓글을 달아 주세요

페이스북 친구들과 댓글을 공유하고 싶다면 아래를 이용해주세요.