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를 사용해도 사실 돌아가긴 한다.

sowhat4.tistory.com/73

 

Junit5 @BeforeEach private method가 동작하는 이유

발단 어느 날과 다름없이 비즈니스 로직을 먼저 작성하고 테스트 코드를 작성하는 코드 몽키 ing 중 🐒 테스트 코드 실행 전 초기 설정이 필요하여 Junit5의 @BeforeEach 사용 호기심이 발동하여 접근

sowhat4.tistory.com

 

 

일반적으로 테스트를 실행하면 테스트에 이름을 표기하는 방법은 아래와 같다. (기본 표기 전략은 메소드 이름)

메소드 이름은 길어지면 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 5 User Guide

Although the JUnit Jupiter programming model and extension model will not support JUnit 4 features such as Rules and Runners natively, it is not expected that source code maintainers will need to update all of their existing tests, test extensions, and cus

junit.org

 

기본적으로 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

 

JUnit 5 User Guide

Although the JUnit Jupiter programming model and extension model will not support JUnit 4 features such as Rules and Runners natively, it is not expected that source code maintainers will need to update all of their existing tests, test extensions, and cus

junit.org

 

+ Recent posts