Jnuit5 - java8 이상을 필요로 하고 단위테스트를 작성할 때 사용한다.
junit4는 써보진 않았지만, 하나의 jar dependency library 형태로 들어오고, junit이 참조하는 다른 라이브러리들이 있는 그런 형태였다면,
junit5는 그 자체로 여러 모듈화(junit platform, jupiter, vintage)가 되어있다.
- Platform: 테스트를 실행해주는 런처 제공. TestEngine API 제공 => 런쳐를 통해 콘솔이나 java 메인 메소드, intellij 등에서 테스트 실행 가능
- Jupiter: TestEngine API 구현체로 JUnit 5를 제공
- Vintage: JUnit 4와 3을 지원하는 TestEngine 구현체
스프링 부트 프로젝트에는 기본적으로 junit5의존성이 추가되어 있다.
기본적인 어노테이션은 아래와 같다.
- @Test
- @BeforeAll - test class 안에 있는 여러 테스트가 모두 실행 될 때, 모든 테스트가 실행되기 전에 한 번만 호출. 구현할 때에는 반드시 static 메소드로 구현해야 하고, private는 불가능하다. 또한, 리턴 타입이 있으면 안됨
- @AfterAll - 마찬가지로 모든 테스트가 실행 된 이후 한 번만 호출. static void 형태로 작성한다.
- @BeforeEach - 모든 테스트를 실행할 때, 각각의 테스트를 실행하기 이전에 한 번씩 호출된다. 마찬가지로 리턴 타입은 void이어야 하며 private를 사용하지 않도록 한다.
- @AfterEach - 마찬가지로 모든 테스트 이후 각각 실행된다. 리턴 타입은 void, private는 사용하지 않도록 한다.
- @Disabled - 필요 없는 테스트 등을 빼고 실행할 때 사용
공식문서에서는 private를 `must not`으로 사용하지 말라고 나와있지만,, 리플렉션 때문에 private를 사용해도 사실 돌아가긴 한다.
일반적으로 테스트를 실행하면 테스트에 이름을 표기하는 방법은 아래와 같다. (기본 표기 전략은 메소드 이름)
메소드 이름은 길어지면 snake case로 작성한다.
- @DisplayNameGeneration: 메소드와 클래스 레퍼런스를 사용해서 테스트 이름을 표기하는 방법 설정하는 방법으로, 기본 구현체로 ReplaceUnderscores 제공 (ex.@DisplayNameGeneration(DisplayGenerator.ReplaceUnderscores.class))
- @DisplayName: 어떤 테스트인지 테스트 이름을 보다 쉽게 표현할 수 있는 방법을 제공하는 애노테이션이다. @DisplayNameGeneration 보다 우선 순위가 높다.
한 테스트에서 여러 Assertion문이 있는 경우에는 앞에 있는 assert문이 실패할 경우 그 다음을 보지 않는데,
이 것을 한번에 실행해 주는 방법이 assertAll이다. 각 assert문을 람다식으로 묶어 주면 한번에 실행하여 결과를 알 수 있다.
assertAll(
() -> assertNotNull(study),
() -> assertEquals(StudyStatus.DRAFT, study.getStatus(), () -> "스터디를 처음 만들면" + StudyStatus.DRAFT + "상태이다."),
() -> assertTrue(study.getLimit()>0, "스터디 최대 참석 가능 인원은 0명 이상이다.");
);
참고로, assert문이 실패했을 때 메세지를 supplier를 사용하여 나타내게 되면 테스트가 실패할 때만 연산이 일어나기 때문에 성능상 조금 더 이점이 있다.
이와 유사하게 테스트 태깅 등으로 그룹화를 할 수 있다.
- @Tag: 테스트를 그룹화 ex) 테스트가 오래 걸려서 로컬에서 테스트하기 적절하지 않는지/테스트가 짧게 끝나는지 등
- 실행 방법
- intellij에서 edit contiguration > test kind를 Tags로 바꾸기 > tag expression 설정해 주기
- maven 설정: maven-surefire-plugin의 그룹을 설정해주기
- 실행 방법
조건에 따라 테스트를 실행할 수도 있다. (특정한 OS, 특정한 java version, 환경 변수 등)
- assumeTrue(조건): 예를 들어 테스트의 환경변수가 로컬일 경우에만 실행할 때, 아래와 같이 진행할 수 있다.
assumeTrue("LOCAL".equalsIgnoreCase(System.getenv("TEST_ENV")));
/*테스트*/
- assumingThat(조건, 테스트)
assumingThat("LOCAL".equalsIgnoreCase(System.getenv("TEST_ENV")), /*테스트*/);
- @EnabledXXX 와 @DisabledXXX
- OnOS: OS 종류
- OnJre: java 버전
- IfSystemProperty
- IfEnvironmentVariable: 환경변수
- If
junit5에서 제공하는 애노테이션들은 메타애노테이션들을 사용할 수 있는데, 그렇기 때문에 composed 애노테이션을 만들어서 사용할 수 있다고 한다.
즉, 커스텀 애노테이션들을 만들 때, 그 애노테이션 위에 기존의 메타애노테이션들을 사용하게 되면, 기존의 기능이 적용된다고 할 수 있다.
이 외에도 파라미터를 여러 인자값을 주면서 테스트를 반복할 수 있다.
junit.org/junit5/docs/current/user-guide/#writing-tests-parameterized-tests
기본적으로 junit이 테스트를 실행할 때, 클래스의 인스턴스를 만들 때 기본 전략은 테스트 메소드 마다 생성하게 된다.
테스트 순서는 예측할 수 없다. 기본적으로 메소드가 선언되어있는 순서대로 실행되긴 하지만,,, 꼭 그런건 아니라고 한다.
그래서 테스트 간 의존성을 없애기 위해서, 서로 공유하는 값을 바뀌지 않게 하기 위해서 각각 인스턴스를 만들어서 테스트를 실행하게 된다.
junit5부터는 이 기본 전략을 바꿀 수 있는 방법이 있다.
- @TestInstance(Lifecycle.PER_CLASS)
- 테스트 클래스당 인스턴스를 하나만 만들어 사용한다. => 이 경우 BeforeAll/AfterAll이 static 메소드일 필요가 없어진다
- 경우에 따라, 테스트 간에 공유하는 모든 상태를 @BeforeEach 또는 @AfterEach에서 초기화 할 필요가 있다.
- @BeforeAll과 @AfterAll을 인스턴스 메소드 또는 인터페이스에 정의한 default 메소드로 정의할 수도 있다.
아까 말했듯이, 테스트 순서는 정해져 있지 았다. 그래서 테스트를 실행할 순서를 정의해서 상태 정보를 유지하며 usecase를 테스트할 수 있는 방법도 있다.
그렇게 하기 위해서는 위 방법처럼 TestInstance 애노테이션으로 의존성을 공유하는 것이 먼저이다.
그 다음 @TestMethodOrder 애노테이션을 클래스에 붙여준다. 기본 구현체는 아래 3가지를 제공해 준다.
- Alphanumeric
- OrderAnnoation: @Order(int), 낮은 값부터 실행시킨다
- Random
마지막으로 junit5에서 extension을 사용하는 방법은 아래 공식문서를 보고 좀 더 공부해봐야겠다.
junit.org/junit5/docs/current/user-guide/#extensions
'Java (+ Spring)' 카테고리의 다른 글
도커로 테스트 하기: TestContainers (0) | 2021.02.10 |
---|---|
Mock객체로 테스트 하기: Mockito (0) | 2021.02.10 |
Spring Boot: Spring Security & 카카오 로그인 API(...ing) (0) | 2020.10.06 |
Spring boot: JPA 설정 방법 및 주의할 점 (1) | 2020.07.31 |
Spring: Spring MVC 설정 하기 (0) | 2020.07.26 |