Java, 테스트 코드 작성 - JUnit 5
작성일
JUnit 5 소개
JUnit 5란?
- 자바 개발자가 가장 많이 사용하는 테스팅 프레임워크
- 단위 테스트를 작성하는 자바 개발자 93%가
JUnit
을 사용
- 단위 테스트를 작성하는 자바 개발자 93%가
- 자바 8 이상을 필요로 함
Junit 5의 세부 모듈 3가지
- JUnit Platform: 테스트 코드를 실행해주는 런처를 제공. TestEngine API 제공
- Jupiter: TestEngine API 구현체로 JUnit 5를 제공
- Vintage: JUnit 4와 3을 지원하는 TestEngine 구현체
JUnit 5: 시작하기
참고
Junit 5부터는 public을 안붙여도 된다. (리플렉션을 사용하기 때문에 public 을 굳이 붙일 필요가 없어짐)
기본 어노테이션
어노테이션을 사용하는 메소드를 구현할 때는 반드시 static
을 붙여야 한다.
default 는 되고 private 은 안된다. 그리고 리턴타입이 있으면 안됨.
→ 기본적으로 static void 써야한다고 생각하자
@Test
- 테스트 메서드임을 나타내는 어노테이션
@BeforeAll / @AfterAll
-
beforeAll 어노테이션: 테스트 클래스 안에 있는 모든 테스트가 실행되기 전 딱 한번 호출 됨
-
afterAll 어노테이션: 테스트 클래스 안에 있는 모든 테스트가 실행된 후 딱 한번 호출 됨
@BeforeEach / @AfterEach
-
beforeEach 어노테이션: 각각의 테스트 메서드가 실행되기 전 호출 됨
-
afterEach 어노테이션: 각각의 테스트 메서드가 실행된 후 호출 됨
@Disabled
- 테스트 어노테이션이 있어도 Disabled 어노테이션이 붙어있다면 테스트코드가 실행되지 않음
테스트 예제
package me.yes.thejavatest;
import org.junit.jupiter.api.*;
import static org.junit.jupiter.api.Assertions.*;
class StudyTest {
@Test
void create() {
Study study = new Study();
assertNotNull(study);
System.out.println("create");
}
@Test
void create1() {
System.out.println("create1");
}
@BeforeAll
static void beforeAll() {
System.out.println("before all");
}
@AfterAll
static void afterAll() {
System.out.println("after all");
}
@BeforeEach
void beforeEach() {
System.out.println("before each");
}
@AfterEach
void afterEach() {
System.out.println("after each");
}
@Test
@Disabled
void create2() {
System.out.println("create2");
}
}
JUnit 5: 테스트 이름 표기하기
- @DisplayNameGeneration
- @DisplayName
테스트 예제
package me.yes.thejavatest;
import org.junit.jupiter.api.*;
import static org.junit.jupiter.api.Assertions.*;
@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)
class StudyTest {
@Test
@DisplayName("스터디 만들기")
void create() {
Study study = new Study();
assertNotNull(study);
System.out.println("create");
}
}
JUnit 5: Assertion
org.junit.jupiter.api.Assertions.*
를 사용
자주 사용하는 Assert문
assertEquals(expected, actual)
- 실제 값이 기대한 값과 같은지 확인
- 기대하는 값을 왼쪽에 실제 나오는 값을 오른쪽에 적어줌 (필수는 아니지만 api가 의도하는 바는 그렇다)
람다식으로 표현하는 이유
- 테스트가 실패 했을 때만 연산 (람다식을 사용하지 않은 코드는 테스트를 실패하던 성공하던 무조건 실행)
- 문자열 연산의 비용이 조금 걱정이 될 때는 람다식을 쓰는게 유리 (성능을 신경쓰는 입장에서)
assertEquals(StudyStatus.DRAFT, study.getStatus(), "스터디를 처음 만들면 상태값이 DRAFT여야 한다.");
assertEquals(StudyStatus.DRAFT, study.getStatus(), () -> "스터디를 처음 만들면 상태값이 DRAFT여야 한다.");
assertEquals(StudyStatus.DRAFT, study.getStatus(),
"스터디를 처음 만들면 상태값이 " + StudyStatus.DRAFT + "여야 한다.");
assertEquals(StudyStatus.DRAFT, study.getStatus(),
() -> "스터디를 처음 만들면 상태값이 " + StudyStatus.DRAFT + "여야 한다.");
assertEquals(StudyStatus.DRAFT, study.getStatus(), new Supplier<String>() {
@Override
public String get() {
return "스터디를 처음 만들면 " + StudyStatus.DRAFT + " 상태다.";
}
});
assertNotNull(actual)
- 값이 null이 아닌지 확인
assertTrue(boolean)
- 다음 조건이 참(true)인지 확인
assertAll(executablesl…)
- 모든 확인 구문 확인
- 테스트는 순차적으로 실행되기 때문에 위에서 테스트가 깨지면 그 다음 assert문으로 넘어가지 않지만 이를 동시에 알 수 있는 방법이 있다.
- 각각 assert문을 실행 할 수도 있지만 모든 assert문의 결과를 한번에 알고 싶을 때
assertAll
을 사용해서 람다식으로 표현하면 assert문을 한번에 실행 할 수 있다.
assertAll(
() -> assertNotNull(study),
() -> assertEquals(StudyStatus.DRAFT, study.getStatus(), () -> "스터디를 처음 만들면 상태값이 DRAFT여야 한다."),
() -> assertTrue(study.getLimit() > 0, "스터디 최대 참석 가능 인원은 0보다 커야 한다.")
);
assertThrows(expectedType, executable)
- 예외 발생 확인
IllegalArgumentException exception =
assertThrows(IllegalArgumentException.class, () -> new Study(-10));
assertEquals("limit은 0보다 커야 한다.", exception.getMessage());
assertTimeout(duration, executable)
- 특정 시간 안에 실행이 완료되는지 확인
- 위의 코드는 테스트가 끝날때까지 테스트가 끝나지 않는다. 즉, 실제 테스트에 오래걸리는 코드가 있다면 그 코드가 끝날때까지 끝나지 않는다. 그러나, 이렇게 하는 것은 비효율적이다. 실제 100밀리세컨드가 끝나면 그냥 테스트가 실패하게 만들고 싶다.(이게 효율적이니까)
- 그럴때 사용하는 것이 assertTimeoutPreemptively() 이다. (Preemptively: 즉각적인)
- 그러나, assertTimeoutPreemptively()는 주의해서 사용해야한다. ThreadLocal과 관련해 예상하지 못한 예외가 발생할 수 있다.
assertTimeout(Duration.ofMillis(100), () -> {
new Study(10);
Thread.sleep(300);
});
assertTimeoutPreemptively(Duration.ofMillis(100), () -> {
new Study(10);
Thread.sleep(300);
});
org.assertj.core.api.Assertions.assertThat
Study actual = new Study(10);
assertThat(actual.getLimit()).isGreaterThan(0);
이렇게 테스트 하는 방법도 있다.
JUnit 5: 조건에 따라 테스트 실행하기
특정한 조건을 만족하는 경우에 테스트를 실행하는 방법
org.junit.jupiter.api.Assumptions
- assumeTrue(조건)
- assumingThat(조건, 테스트)
@Enabled 와 @Disabled
- OnOS
- OnJre
- IfEnvironmentVariable
- If
테스트 예제
package me.yes.thejavatest;
import org.junit.jupiter.api.*;
import org.junit.jupiter.api.condition.*;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assumptions.assumeTrue;
import static org.junit.jupiter.api.Assumptions.assumingThat;
class StudyTest {
@Test
@DisplayName("스터디 만들기")
@EnabledOnOs({OS.MAC, OS.LINUX})
@EnabledOnJre({JRE.JAVA_8, JRE.JAVA_9, JRE.JAVA_10, JRE.JAVA_11})
@EnabledIfEnvironmentVariable(named = "TEST_ENV", matches = "LOCAL")
void create() {
String test_env = System.getenv("TEST_ENV");
assumeTrue("LOCAL".equalsIgnoreCase(test_env));
assumingThat("LOCAL".equalsIgnoreCase(test_env), () -> {
Study actual = new Study(10);
assertThat(actual.getLimit()).isGreaterThan(0);
});
}
}