[Java] Junit5 & 테스트코드

한호성·2024년 5월 28일
0

자바_기본개념

목록 보기
3/3

Introduction

자바 기본기 다시 정리하면서, 놓쳤던 부분을 정리하고자 합니다.
백기선 님의 자바 온라인스터디 4주차 내용입니다.

온라인스터디 과제 중 제가 학습에 필요한것들만 추려서 정리하였습니다.

JUnit5

  • JUnit은 자바 진영에서 테스트하기 위해 사용되는 Framework 중 하나이다.
  • JUnit5은 Java8 스타일의 코딩을 적용하고 JUnit4 보다 유연하게 만드는 것을 목표라 개발되었다고 합니다.

(어떤 차이점이 있는지, 알아봅시다.)

JUnit4 vs JUnit5

JUnit4의 문제점

  • 부족한 확장성 (하나의 @Runwith에 여러 Rule을 사용하여 확장하고, Runwith들이 결합되지 못했다.)
  • IDE에 대한 강한 결합도
  • 하나의 Jar의 큰 책임

-> 솔직히 JUnit5부터 사용하다보니.. 위의 문제점을 느끼지는 못했지만.. 이런것들 있었구나 정도는 알고 쓰도록 하자.

차이점

Architecture

  • JUnit4은 모든 것이 하나의 Jar 파일로 묶여서 포함되어 있습니다.

  • JUnit5은 JUnit Platform, JUnit Jupiter, JUnit Vintage의 3가지 하위 프로젝트로 구성됩니다.

  • JUnit Platform : Platform에서 실행되는 새로운 테스트프레임워크를 개발하기 위한 TestEngine Api를 정의하고 있습니다.

  • JUnit Jupiter : 새로운 Annotation으로 작성된 테스트를 실행하기 위한 JUnit annotations와 TestEngine을 제공합니다.

  • JUnit Vintage : JUnit5 플랫폼에서 3,4버전으로 작성된 테스트코드를 실행할 수 있도록 지원합니다.

실제로 제가 사용하고 있는 라이브러리를 캡쳐해보았습니다. Platform, Jupiter 패키지가 존재하는 것을 확인할 수 있었습니다.

JDK 요구사항

  • JUnit4 - Java5 이상
  • JUnit5 - Java8 이상

Non-public Test Methods허용

  • JUnit5에서는 테스트 클래스와, 테스트메서드를 public으로 하지 않아도 된다.
    Junit5은 내부적으로 리플렉션을 사용하여 테스트 클래스와 테스트 메서드를 찾습니다.

Annotation의 변경

JUnit4: @Before, @After, @BeforeClass, @AfterClass, @Test
JUnit5: @BeforeEach, @AfterEach, @BeforeAll, @AfterAll, @Test
으로 바뀌었습니다.

위의 annotation들이 테스트 생명주기와 관련이 있으니 간단히 이야기해봅시다.

@BeforeAll : 현재 클래스의 모든 테스트 메서드 전에 1번 실행
@AfterAll : 현재 클래스의 모든 테스트 메서드 다 실행 후 1번실행
@BeforeEach : 각 테스트 메서드 전에 1번씩 실행
@AfterEach : 각 테스트 메서드 실행 후 1번씩 실행

#cf) 위의 특성을 사용해서 @Transcational 롤백을 사용하지 않고, 실제 H2-DB에 Test에 필요한 데이터들을 세팅하고, 테스트를 한 후 데이터를 지운적이 있다.

다양해진 assert 방식과 한번에 여러 Test가능

@Test
void testCalculator() {
  //기본
  assertEquals(2, calculator.add(1,1));
  assertEquals(5, calculator.mul(2,5), "실패 메시지(optional)");
  
  //그룹
  assertAll(() -> assertEquals(3, calculator.sub(5,2)),
            () -> assertEquals(1, claculator.div(5,5)));
  
  //의존
  assertAll(() -> {
             assertAll(() -> assertTrue(),
                       () -> assertTrue());
           },
           () -> {
             assertAll(() -> assertEquals(),
                       () -> assertEquals());
           });
  
  //예외
  Exception e = assertThrows(ArithmeticException.class, () -> calculator.div(1, 0));
  assertEquals("/ by zero", e.getMessage());
  
  //제한시간
  assertTimeout(ofMinutes(2), () -> {
    // 2분 미만의 로직만 통과
  });
}

동적테스트 지원

기존에 우리가 작성하는 코드들은 정적 테스트 코드이다.

ex)
@ParameterizedTest
@ValueSource(ints = {1, 2, 3, 4, 5})
void isUnderTenTest(int number) {
  boolean result = isUnderTen(number);
  
  assertThat(result).isTrue();
}

동적테스트 코드는 Runtime에 생성되는 테스트를 수행한다는 것이다. 차이가 잘 와닿지 않는다.

코드를 보자

    @TestFactory
    Stream<DynamicTest> isUnderTenTest() {
        List<Integer> numbers = getNumberFromDatabase(); // 1, 2, 3, 4, 5, 6, 7, 8, 9

        return numbers.stream()
                .map(num -> dynamicTest(num + "가 10미만인지 검사",
                        () -> {
                            boolean result = num < 10;
                            assertThat(result).isTrue();
                        }
                ));
    }

어떤게 가장 큰 차이일까? 다이나믹 테스트의 강력함은 테스트 케이스가 동적으로 생성되고 수행되기 때문에, 여러 dynamicTest안에서 데이터 결과를 공유하고 연속성 있는 테스트를 작성할 수 있다는 점이다.

또한 통합테스트를 할 때, 테스드들을 묶고, 데이터를 공유하면서 작성할 때 좋은 방법으로 사용될 수 있어보인다.

조건부 테스트 (Conditional Test)

JUnit5: 특정 조건에서만 테스트를 실행할 수 있도록 @EnabledIf, @DisabledIf 등의 애노테이션을 제공합니다.

필자는 맥북을 사용하는데 테스트가 ignored 되는 것을 볼 수 있엇다.

Reference

https://junit.org/junit5/docs/current/user-guide/#overview
https://howtodoinjava.com/junit5/junit-5-vs-junit-4/
https://tecoble.techcourse.co.kr/post/2020-07-31-dynamic-test/

profile
개발자 지망생입니다.

0개의 댓글