[영상후기] [10분 테코톡] 🌊 바다의 JUnit5 사용법

박철현·2023년 12월 31일
0

영상후기

목록 보기
143/160

movie

JUnit5 및 개요

  • JUnit : Java 8부터 제공하는 단위테스트 프레임워크

    • 스프링부트 2.2 부터 기본 제공
  • JUnit 5란?

    • Platform을 통해 런처가 실행되고, JUmit 5는 Jupiter, JUnit 3, 4는 Vintage로 실행
      • Jupiter : JUnit 5를 지원하는 TestEngine API 구현체
      • Vintage : JUnit 4와 3을 지원하는 TestEngine 구현체
      • Platform : 테스트를 실행해주는 런처 제공, TestEngine API 제공
  • JUnit 5 시작하기

    • 스프링 부트 2.2버전 이상부터는 기본적으로 JUnit5 의존성 추가
    • 스프링 부트 프로젝트가 아닌 경우 의존성 추가
      • junit-jupiter-engine-5.6.2

JUinit5 어노테이션

  • @Test

    • 테스트 메서드라는 것을 나타내는 어노테이션
    • JUnit4와 다르게 어떠한 속성도 선언하지 않는다
      • Jupiter에서는 해당 어노션들이 이미 존재하기 때문
    // JUnit4
    @Test(expected = Exception.class)
    void create() throws Exception {
    ...
    }
    
    //JUnit5
    @Test
    void create() {
    ...
    }
  • 생명주기 어노테이션

    • @BeforeAll : 해당 클래스에 위치한 모든 테스트 메서드 실행 전에 딱 한번 실행되는 메서드
    • @AfterAll : 해당 클래스에 위치한 모든 테스트 메서드 실행 후 딱 한번 실행되는 메서드
      • JUnit4의 @BeforeClass / @AfterClass와 유사
    • @BeforeEach : 해당 클래스에 위치한 모든 테스트 메서드 실행 전에 실행되는 메서드
    • @AfterEach : 해당 클래스에 위치한 모든 테스트 메서드 실행 후에 실행되는 메서드
      • JUnit4의 @Before / @After와 유사
      • 매 테스트 메서드마다 새로운 클래스를 생성하여 실행(비효율 적)
    • @BeforeAll은 한번만 실행 / @BeforeEach는 여러번 실행
      • 테스트가 조건들에 영향을 미친다면 매 테스트마다 조건을 초기화 해주는 BeforeEach가 좋음
  • @Disabled

    • 테스트를 하고 싶지 않은 클래스나 메서드에 붙이는 어노테이션
      • 주석처리 하지 않고도 어노테이션으로 테스트 실행되지 않게 중단할 수 있음
    • JUnit4의 @Ignore과 유사
    class DisabledExample {
      @Test
      @Disabled("문제가 해결될 때까지 테스트 중단")
      void test() {
    	  System.out.println("테스트");
      }
      @Test
      void test2() {
    	  System.out.println("테스트2);
      }
    }
    
  • @DisplayName

    • IDE나 빌드툴에서 표시할 테스트 이름을 지정할 수 있음
    • 공백, 이모지, 특수문자 등을 모두 지원
      @Test
      @DisplayName("100, 100, {10, 10} → 102")
      void t02() {
          assertThat(
                  new Solution().solution(100, 100, new int[]{10, 10})
          ).isEqualTo(
                  102
          );
      }
  • @RepeatedTest

    • 특정 테스트를 반복시키고 싶을 때 사용하는 어노테이션
    • 반복 횟수와 반복 테스트 이름을 설정 가능
      • 성능적 이슈 확인할 때 사용
    @RepeatedTest(10)
    @DisplayName("반복 테스트")
    void repeatedTest() {
    ...
    }
    // 각 iteration의 display name을 커스텀하게 출력할 수 있음
    @RepeatedTest(value = 10, name = "{displayName} 중 {currentRepetition} of {totalRepetitions}")
    @DisplayName("반복 테스트")
    void repeatedTest2() {
    ...
    }
  • @ParameterizedTest

    • 테스트에 여러 다른 매개변수를 대입해가며 반복 실행할 때 사용하는 어노테이션
    • @CsvSource : 테스트 데이터를 CSV 형식으로 제공
    • 구분자로 설정한(여기서는 콜론(:)) 문자로 구분하여 매개변수에 넣어 JUnit이 자동으로 메소드 호출
      • @ParameterizedTest 어노테이션을 사용한 테스트 메소드의 매개변수로 전달됨
      • 아래의 순서로 JUnit이 자동으로 테스트 메서드 호출
        • calculateCardSumWhenAceIsTwo("ACE, ACE", 12)
        • calculateCardSumWhenAceIsTwo("ACE, ACE, ACE", 13)
        • calculateCardSumWhenAceIsTwo("ACE, ACE, TEN", 12)
    @ParameterizedTest
    @CsvSource(value = {"ACE, ACE:12", "ACE, ACE, ACE:13", "ACE, ACE, TEN:12", delimiter = ":")
    @DisplayName("에이스 카드가 여러 개일 때 합 구하기")
    void calculateCardSumWhenAceIsTwo(final String input, final int expected) {
    	final String[] 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와 같은 테스트 생명주기에 관계된 메소드들도 계층에 맞춰 동작

    @DisplayName("findResult 메서드는")
    @Nested
    class findResult {
    	@DisplayName("플레이어가 블랙잭일 때")
      @Nested
      class findWhenPlayerBlackjack {
      	private Result findResult(){
          ...
          }
      }
      
      @DisplayName("딜러가 블랙잭이면 DRAW 반환한다")
      @Test
      void returnDraw() {
      	...
        }
      }

Assertions

  • Assertions

    • 사전적 의미 : 주장, 행사, 단정문
    • 테스트 케이스의 수행 결과를 판별하는 메서드
    • 모든 Junit Jupiter Assertions는 static 메서드
  • JUnit5 추가된 메서드

    • assertAll(executables...)

      • 매개변수로 받는 모든 테스트코드를 한 번에 실행
      • 오류가 나도 끝까지 실행한 뒤 한 번에 모아서 출력
      // 기존에는 중간에 오류나면 남은 assert 메서드 실행하지 않음
      // ex) 2번째 assertEquals에서 오류나면 assertTrue는 실행하지 않고 실패 처리
      @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보다 커야한다.")
          );
      }
      
    • asserThrows(expectedType, executable)

      • 예외 발생을 확인하는 테스트
      • executable의 로직 실행 중 expectedType의 에러를 발생시키는지 확인
      //  JUnit4
      // 단순히 예외 발생 여부만 검증
      @Test(expected = Exception.class)
      void create() throws Exception {
      ...
      }
      
      // JUnit5
      // 예외 발생을 검증하고 예외를 반환받아서 예외의 상태까지 검증 가능
      // assertDoesNotThrow : 예외가 발생하지 않았는지 검증하는 단정문
      @Test
      void exceptionThrow() {
      	Exception e = assertThrows(Exception.class, () -> new Test(-10)); // 람다 표현식 뒤에가 예외가 있는지 검증
          assertDoesNotThrow(() -> System.out.println("Do Something"));  // Junit 5.2부터 추가됨, 람다 표현식 뒤에 부분 실행 결과가 예외가 없는지 검증
      }
    • assertTimeOut(duration, executable)

      • 특정 시간 안에 실행이 완료되는지 확인
      • Duration : 원하는 시간
      • Executable : 테스트 할 로직
    @Rule
    public Timeout timeout = Timeout.seconds(5); // 이전 버전에서 Rule 어노테이션으로 검증 가능하긴 했음
    // 테스트 메소드 전체에 대한 시간 제한만 가능하다는 단점
    // 특정 부분의 실행 시간 제한 불가
    
    class TimeoutExcample {
      @Test
      @DisplayName("타임아웃 준수")
      void timeoutNotExceeded() {
      // 단정문을 사용함으로 테스트 메서드 내부 처리 뿐 아니라 데이터 생성 부분과 같은 것들을 좀 더 유연하게 테스트 할 수 있어 사용성 개선
        assertTimeout(ofMinutes(2), () -> Thread.sleep(10));
      }
      @Test
      @DisplayName("타임아웃 초과")
      void timeoutExceeded() {
     	  assertTimeout(ofMillis(10), () -> Thread.sleep(100));
      }
    }

Assumption

  • Assumtion

    • 전제문이 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"); // 단정문이 실행됨

JUnit4와 5차이 한눈에 보기

표현JUnit4JUnit5비고
import문 수정org.junit.*org.junit.jupiter.*jupiter로 변경
어노테이션수정@BeforeClass@BeforeAll
@Before@BeforeEach
@After@AfterEach
@AfterClass@AfterAll
@Ignore@Disabled
AssertAssertAssertionsAssert -> Assertions

|

profile
비슷한 어려움을 겪는 누군가에게 도움이 되길

0개의 댓글

관련 채용 정보