[Java] 테스트 코드(Junit5, AssertJ) - 1

수박이삼촌·2024년 3월 15일
0

Java

목록 보기
20/21
post-thumbnail

실습한 코드는 깃허브 링크에서 확인 가능하십니다.

0.  🐳 테스트 코드

메소드를 개발할 때 다양한 경우를 test 코드를 작성하며 미리 파악할 수 있습니다.
우선, Junit 과 AssertJ의 차이점을 알아보겠습니다.

  • Junit

    • 자바에 구축된 자동화 테스트 가능한 프레임워크입니다.

    • 다양한 어노테이션(@Test, @Before 등)을 사용하여 테스트 케이스 실행과 테스트 환경을 설정합니다.

  • AssertJ

    • 자바 테스트 코드의 가독성과 편의성을 높여주는 라이브러리입니다.
      메서드 체이닝을 지원하여 더 직관적이고 유연한 테스트 코드 작성을 도와줍니다.

    • JUnit의 assertEquals만으로 제한될 수 있는 표현력과 달리, assertThat() 메서드와 함께 filteredOn, containsOnly 등 다양한 표현이 가능해 복잡한 조건의 테스트도 쉽게 구현할 수 있습니다.

0-1. 🐬 Given/When/then

단위 테스트는 Given/When/Then으로 진행됩니다.

  • Given(준비): 어떠한 데이터가 준비되었을 때

  • When(실행): 어떠한 함수를 실행하면

  • Then(검증): 어떠한 결과가 나와야 한다.

1.   🐳 Junit5

Junit5는 세 개의 서브프로젝트로 이루어져 있습니다.

  1. JUnit Platform
    JVM에서 테스트 프레임워크를 실행하기 위한 테스트 엔진을 제공합니다.
  2. JUnit Jupiter
    Junit5에서 테스트를 작성하고 실행하기 위한 엔진을 제공합니다.
  3. JUnit Vintage
    Junit3, 4를 기반으로 돌아가는 테스트 엔진을 제공합니다. 즉, 하위호환용입니다.

이 글에서는 가장 최근 버전인 JUnit5엔진을 사용하겠습니다.

1-1. 🐬 테스트 특징

  1. 테스트 클래스는 적어도 한 개 이상의 @Test 어노테이션이 달린 메소드를 가져야 합니다.

  2. 테스트 클래스는 abstrract이면 안되고, 한 개의 생성자를 가지고 있어야 합니다.
    (두 개 이상 작성하면 런타임 시 PreconditionViolationException이 발생합니다.)

    	public TestAnnotationTests() {}
    
    	public TestAnnotationTests(int value) {}

    실행 결과

    org.junit.platform.commons.PreconditionViolationException: Class [com.unclesubak.section01.test.TestAnnotationTests] must declare a single constructor
    org.junit.jupiter.api.extension.ParameterResolutionException: No ParameterResolver registered for parameter [int arg0] in constructor [public com.unclesubak.section01.test.TestAnnotationTests(int)]
  3. 아무런 생성자도 작성하지 않으면 기본 생성자는 컴파일러가 자동으로 추가해줍니다.

  4. @DisplayName으로 메소드 명이 아닌 설정한 이름으로 표기가 가능합니다.
    (미사용 시 메소드명, 한글표기도 가능합니다.)

    @Test
    @DisplayName("테스트 메소드1")
    public void testMethod1() {
    
    }

    실행 결과

  5. @DisplayNameGenerator
    클래스 레벨에 @DisplayNameGenerator를 붙이게 되면 언더 바를 공백으로 처리하여 테스트 이름을 부여해줍니다. 단, @DisplayName과 중복 작성 시에는 @DisplayName에 부여한 테스트 이름이 우선권을 가집니다.

     @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)
    	public class TestAnnotationTests {
             
             @Test
             public void 테스트_메소드2() {
             }
    
             @Test
             @DisplayName("displayName 우선순위 테스트")
             public void 테스트_메소드3() {
             }
     }

    실행 결과

  6. 각각의 테스트는 실행 순서를 작성 순서로 보장해주지 않습니다.


1-2. 🐬 LifeCycle

테스트 메소드 실행 전과 후에 자동 실행되는 설정을 할 수 있습니다.

  • @BeforeAll : 테스트가 실행되기 전 딱 한 번만 실행됩니다.
  • @BeforeEach : 각각의 테스트 메소드가 실행되기 전 실행됩니다.
    • @Test, @RepeatedTest, @ParameterizedTest, @TestFactory가 실행되기 전에 동작합니다.
    • 주로 테스트 하기 전에 필요한 목업 데이터를 미리 세팅해 줄 목적으로 사용한다.
  • @AfterAll : 모든 단위 테스트가 완전히 끝난 후 딱 한 번만 실행됩니다.
    • @Test, @RepeatedTest, @ParameterizedTest, @TestFactory가 실행된 이후 동작됩니다.
    • 주로 단위 테스트들이 수행된 이후 사용한 자원을 해제할 목적으로 사용합니다.
  • @AfterEach : 각각의 테스트 메소드가 동작한 직후 동작됩니다.

🚨주의해야 할 점

  • 모든 테스트 메소드와 라이프사이클 관련 메소드는 abstract이면 안되고, void 형으로 작성해야 합니다.
  • 접근제한자는 사용하지 않아도 되지만 (default), private이면 안된다.

1-2-1. 🌊 LifecycleAnnotationTests

 	@BeforeAll
    public static void beforeAll() {
        System.out.println("beforeAll");
    }

    @BeforeEach
    public void beforeEach() {
        System.out.println("beforeEach");
    }

    @Test
    public void test1() {
        System.out.println("test1");
    }

    @Test
    public void test2() {
        System.out.println("test2");
    }

    @AfterEach
    public void afterEach() {
        System.out.println("afterEach");
    }

    @AfterAll
    public static void afterAll() {
        System.out.println("afterAll");
    }

실행 결과


1-3. 🐬 Additional

부가적인 테스트 조건을 추가하여 테스트를 진행할 수 있습니다.
JUnit에서 기본적으로 제공하는 어노테이션을 종류 별로 정리해보겠습니다.

1-3-1. 🌊@Disabled

해당 테스트 메소드를 클래스 레벨에서 제외시킵니다.
JUnit4에선 @Ignore로 사용됩니다.

    @Disabled
    @Test
    public void testIgnore() {}

실행 결과


1-3-2. 🌊 @TimeOut

  • 해당 테스트 메소드의 제한 시간 설정을 해줍니다.
  • 주어진 시간 안에 테스트가 끝나지 않으면 테스트가 실패합니다.
  • value에는 시간을 정수형으로, unit에는 사용할 시간 단위를 기술합니다.
    (unit에서는 다음과 같은 설정 값들이 있습니다.)
   @Test
    @Timeout(value = 1000, unit = TimeUnit.MILLISECONDS)
    public void testTimeout() throws InterruptedException {

        Thread.sleep(1100);
    }

실행 결과


1-3-3. 🌊 @Tag

일부 설정으로 특정 Tag들만 테스트할 수 있습니다.
하지만, 규칙이 있습니다.

🚨@Tag를 사용하기 위한 규칙

    1. 태그는 공백이나 null이 있으면 안됨
    1. 다음 글자를 포함하면 안됩니다.
      , ( ) & ! |
	@Test
    @Tag("development")
    public void testTag1() {}

    @Test
    @Tag("production")
    public void testTag2() {}

    @Test
    @Tags(value = {@Tag("development"), @Tag("performance")})
    public void testTag3() {}

설정 방법

  1. 우측 상단 실행버튼 옆 드롭다운 박스를 클릭하여 Edit Configurations를 선택합니다.

  2. 모듈을 해당 프로젝트의 테스트 경로로 설정합니다. (프로젝트로 설정하면 버퍼 용량 초과 에러 발생합니다.)

  3. Debug/Run Configurations 창 왼쪽 상단에 + 버튼을 눌러서 JUnit을 추가합니다.

  4. 필터 이름 설정 후 build and run 부분의 세 번째 드롭다운박스를 선택해서 Tags를 선택하고 필터링할 태그 이름을 입력합니다.

  5. 실행하려 하면 설정을 다시 할 것인지 묻는 창이 나오는데 무시하고 그냥 실행하면 해당 태그로 필터링된 테스트만 실행하게 됩니다.

실행 결과


1-3-4. 🌊 @Order

테스트 메소드는 원래는 실행 순서를 보장해주지 않습니다.
하지만 일부 테스트 환경(ex - 통합테스트)에서는 테스트 메소드의 순서가 중요한 경우가 있다고 합니다.

설정 방법

  1. 클래스 레벨에 @TestMethodOrder(MethodOrderer.OrderAnnotation.class) 어노테이션을 추가합니다.

  2. 각 테스트 메소드에 @Order 어노테이션으로 순서를 지정하면 테스트 순서를 설정할 수 있습니다.
    (클래스에 작성한 테스트 메소드의 순서는 MethodOrderer에 DisplayName, MethodName, OrderAnnotation, Random 등이 있습니다.)

    @Test
    @Order(1)
    public void testFirst() {}

    @Test
    @Order(2)
    public void testSecond() {}

    @Test
    @Order(3)
    public void testThird() {}

실행 결과

before

after


1-3-5. 🌊@RepeatedTest

해당 테스트 메소드를 반복 실행해서 테스트할 수 있습니다.

  • RepeatedTest는 명시된 숫자로 얼마나 테스트를 반복할 것인지를 지정해서 사용합니다.
  • 반복된 테스트 메소드의 호출은 보통의 @Test 메소드들이랑 똑같이 동작합니다.
    @RepeatedTest(10)
    public void testRepeat() {}

실행 결과


1-4. 🐬 Enviroment

특정 운영체제, JRE, 환경변수에 따라 테스트 수행을 할 수 있습니다.

1-4-1. 🌊 OS(운영 체제)

  • @EnabledOnOs : 특정 OS 환경에서만 테스트가 수행됩니다.
  • @DisabledOnOS : 특정 OS 제외한 환경에서만 테스트가 수행됩니다.

이 때, 특정 OS에서 무시되는 사유를 disabledReason에 기술하여 테스트코드가 무시되는 사유를 다른 사람도 함께 알아볼 수 있도록 작성해줄 수 있습니다.

🚨설정 가능한 OS들!!

    @Test
    @EnabledOnOs(value = OS.MAC, disabledReason = "맥 OS 환경에서만 테스트가 가능합니다.")
    public void testMAC() {}

    @Test
    @EnabledOnOs(value = OS.WINDOWS, disabledReason = "Window 환경에서만 테스트가 가능합니다.")
    public void testWindow() {}

    @Test
    @EnabledOnOs(value = {OS.LINUX, OS.WINDOWS}, disabledReason = "Linux 혹은 Window 환경에서만 테스트가 가능합니다.")
    public void testLinuxAndWindow() {}

    @Test
    @DisabledOnOs(value = OS.MAC, disabledReason = "MAC 환경에서는 테스트가 불가능합니다.")
    public void testDisabledOnMAC() {}

실행 결과

저는 MAC 환경에서 테스트를 진행하여 다음과 같은 결과가 나왔습니다.

1-4-2. 🌊 JRE

  • @EnabledOnJre : 특정 JRE 버전에서만 테스트가 수행됩니다.
  • @DisabledOnJre : 특정 JRE을 제외한 버전에서만 테스트가 수행됩니다.
  • @EnabledOnForJreRange : min과 max 속성 사이의 버전에서 테스트가 수행됩니다.
    • min만 작성 시 min~최신버전까지,
      max만 작성 시 이전 버젼부터 max까지만 테스트합니다.
  • @DisabledOnForJreRange : min과 max 속성 사이를 제외한 버전에서 테스트가 수행됩니다.
    @Test
    @EnabledOnJre(value = JRE.JAVA_8, disabledReason = "JRE 1.8 환경에서만 테스트가 수행됩니다.")
    @Order(5)
    public void testJRE8() {}

    @Test
    @EnabledOnJre(value = {JRE.JAVA_8, JRE.JAVA_11}, disabledReason = "JRE 1.8 과 1.11 환경에서만 테스트가 수행됩니다.")
    @Order(6)
    public void testJRE8AndJRE11() {}

    @Test
    @DisabledOnJre(value = JRE.JAVA_11, disabledReason = "JRE 1.11를 제외한 환경에서만 테스트가 수행됩니다.")
    @Order(7)
    public void testDiabledOnJRE11() {}

    @Test
    @EnabledForJreRange(min = JRE.JAVA_8, max = JRE.JAVA_17)
    @Order(8)
    public void testFromJRE8To17() {}

    @Test
    @DisabledForJreRange(min = JRE.JAVA_8, max = JRE.JAVA_17)
    public void testDiabledFromJRE8To17() {}

실행 결과

1-4-3.🌊 Enviroment Variable

  • 운영체제의 시스템 환경변수에 따라 테스트를 활성화 또는 비활성화 할 수 있습니다.

    • Window의 경우, cmd에서 set 명령으로 시스템 환경변수값을 확인해볼 수 있습니다.
    • MAC의 경우, 터미널에서 env 입력하면 조회가 가능합니다.
    • 시스템 변수의 이름은 대소문자를 구분하지 않지만 값은 대소문자를 구분한다.
      @Test
      @EnabledIfEnvironmentVariable(named = "USER", matches = "kimjong-wan")
      public void testEnviromentVariable() {}
    
      @Test
      @DisabledIfEnvironmentVariable(named = "USER", matches = "kimjong-wan")
      public void testDisabledEnviromentVariable() {}

    실행 결과


1-5.🐬Custum

test 어노테이션을 직접 생성해서 사용할 수도 있습니다.
예를 들어, 특정 OS에서 사용할 수 있는 테스트 설정이 복잡하니 간단하게 custum을 해보겠습니다.

  @Retention(RetentionPolicy.RUNTIME)
  @Target(ElementType.METHOD)
  @EnabledOnOs(OS.MAC)
  @Test
  public @interface MacTest {
  }
  
  @Retention(RetentionPolicy.RUNTIME)
  @Target(ElementType.METHOD)
  @EnabledOnOs(OS.WINDOWS)
  @Test
  public @interface WindowsTest {
  }
  
  @WindowsTest
  public void testOnWindowsOs() {}
    
  @MacTest
  public void testOnMacOS() {}

실행 결과


📒 References

profile
Today I Learned

0개의 댓글