Junit 기본사용법
Junit 의 개념과 기본을 알아보자
기본 개념
- 자바 개발자의 93%가 사용
- Junit5 는 2017년 10월에 공개
- 스프링 부트 2.2버전 이상부터는 기본으로 제공
Junit4까지는 다른 라이브러리를 참조해서 사용하는 구조였으나, 5부터는 그 자체로 가능하게 됨.
Junit 은 Junit Platform위에 Jupiter / Vintage가 올라가 있다. 셋다 Junit5의 세부 모듈이라 생각하면 된다
- Platform : 테스트를 실행해주는 런처 제공 TestEngineAPI 제공
- Jupiter : Junit5를 지원하는 TestEngine API 구현체
- Vintage : Junit4와 3을 지원하는 TestEngine API 구현체
스프링부트2.2이상부터는 기본적으로 의존성이 추가 된다. 만약 스프링부트 프로젝트가 아닌데 사용할 경우 다음과 같이 추가하면 된다
maven
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.8.2</version>
<scope>test</scope>
</dependency>
gradle
testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.8.2'
Annotations
@Test
- 테스트 메서드 라는 것을 나타내는 어노테이션
- Junit4 와 다르게 어떠한 속성도 선언하지 않는다.
// JUnit4
@Test(expected = Exception.class)
void create() throws Exception {
...
}
// JUnit5
@Test
void create() {
...
}
@BeforeAll / @AfterAll
해당 클래스에 위치한 모든 테스트 메서드 실행 전이나 혹은 후에 딱 한번 실행되는 메서드
Junit4의 @BeforeClass / @AfterClass 와 유사
@BeforeEach / @AfterEach
해당 클래스에 위치한 모든 메서드 실행전 혹은 후에 실행
Junit4의 @Before / @After 와 유사
- 매 테스트 메서드 마다 새로운 클래스를 생성(new)하여 실행(비효율적)
테스트들이 BoforeAll 이후에 어떠한 조건도 변경될 것이 없다면 이렇게 BeforeAll 을 사용하고
@BeforeAll
@Test
@Test
@Test
@Test
@Test
매 테스트마다 조건들이 새롭게 바뀌어야 한다면
이렇게 BeforeEach를 사용
@BeforeEach
@Test
@BeforeEach
@Test
@BeforeEach
@Test
@Disabled
- 테스트를 하고싶지 않은 클래스나 메서드에 붙이는 어노테이션
- Junit4 의 @Ignore 과 유사
class DisabledExample {
@Test
@Disabled("문제가 해결될 때까지 테스트 중단")
void test() {
System.out.println("테스트");
}
@Test
void test2() {
System.out.println("테스트2");
}
}
@DisplayName
- 어떤 테스트인지 쉽게 표현할 수 있도록 해주는 어노테 이션
- 공백, Emoji, 특수문자 등을 모두 지원
@DisplayName("특수 테스트\uD83D\uDE00")
class DisplayNameExample {
@Test
@DisplayName("굉장한 테스트 입니다")
void test() {
}
}
@RepeatedTest
- 특정 테스트를 반복시키고 싶을 때 사용하는 어노테이션
- 반복 횟수와 반복 테스트 이름을 설정가능
- displayName / currentRepetition / totalRepetitions등을 사용해서 반복내용을 커스텀하게 출력 가능
@RepeatedTest(10)
@DisplayName("반복 테스트")
void repeatedTest() {
...
}
@RepeatedTest(value = 10, name = "{displayName} 중 {currentRepetition} of {totalRepetitions})
@DisplayName("반복 테스트")
void repeatedTest2() {
...
}
@ParameterizedTest
- 테스트에 여러 다른 매개변수를 대입해가며 반복 실행 할 때 사용하는 어노테이션
@ParameterizedTest
@CsvSource(value = {"ACE,ACE:12", "ACE,ACE,ACE:13", "ACE,ACE,TET:12"}, delimiter = ':')
@DisplayName("에이스 카드가 여러 개일 때 합 구하기")
void claculateCardSumWhenAceIsTwo(final String input, final int expected) {
final Strings[] inputs = input.split(",");
for (final String number : inputs) {
final CardNumber cardNumber = CardNumber.valueOf(number);
dealer.receiveOneCard(new Card(cardNumber, CardType.CLOVER));
}
assertThat(dealer.calculateScore()).isEqualTo(expected);
}
@Nested
- 테스트 클래스 안에서 내부 클래스를 정의해 테스트를 계층화 할 때 사용
- 내부클래스는 부모클래스의 멤버 필드에 접근 가능
- Before / After 와 같은 테스트 생명주기에 관계된 메소드들도 계층에 맞춰 동작
Assertions
- 사전적 의미 : 주장, 행사, 단정문
- 테스트 케이스의 수행 결과를 판별하는 메서드
- 모든 Junit Jupiter Assertions는 static 메서드이다
assertAll(executables...)
- 매개변수로 받는 모든 테스트 코드를 한 번에 실행
- 오류가 나도 끝까지 실행 한 뒤 한번에 모아서 출력
@Test
public void create_study() {
Study study = new Study();
assertNotNull(study);
assertEquals(Status.STARTED, study.getStatus(), "처음 상태값이 DRAFT");
assertTrue(study.getLimit() > 0, () -> "최대 인원은 0보다 커야한다.");
}
@Test
public void create_study() {
Study study = new Study();
assertAll(
() -> assertNotNull(study),
() -> assertEquals(Status.STARTED, study.getStatus(), "처음 상태값이 DRAFT"),
() -> assertTrue(study.getLimit() > 0, "최대 인원은 0보다 커야한다.")
);
}
assertThrows(expectedType, executable)
- 예외 발생을 확인하는 테스트
- excutable 의 로직이 실행하는 도중 expectedType의 에러를 발생시키는지 확인
// Junit4
@Test(expected = Exception.class)
void create() throws Exception {
...
}
// Junit5
@Test
void exceptionThrow() {
Exception e = assertThrows(Exception.class,() -> new Test(-10));
assertDoesNotThrow(() -> System.out.println("Do Something"));
}
assertTimeout(duration, executable)
- 특정 시간 안에 실행이 완료되는지 확인
- Duration : 원하는 시간
- Excutable : 테스트할 로직
@Rule
public Timeout timeout = Timeout.seconds(5);
class TimeoutExample {
@Test
@DisplayName("타임아 웃 준수")
void timeoutNotExceeded() {
assertTimeout(ofMinutes(2), () -> Thread.sleep(10));
}
@Test
@DisplayName("타임아웃 초과")
void timeoutExceeded() {
assertTimeout(ofMillis(10), () -> Thread.sleep(100));
}
}
Assumption
- 전제문이 true 라면 실행, false 라면 종료
- assumeTrue : false 일 때 이후 테스트 전체가 실행되지 않음
- assumingThat : 파라미터로 전달된 코드블럭만 실행되지 않음
void dev_env_only() {
assumeTrue("DEV".equals(System.getenv("ENV")), () -> "개발 환경이 아닙니다.");
assertEquals("A", "A"); // 단정문이 실행되지 않음
}
void some_test() {
assumingThat("DEV".equals(System.getenv("ENV")), () -> {
assertEquals("A", "B"); // 단정문이 시리행되지 않음
})
assertEquals("A", "A"); // 단정문이 실행됨
}