[Java] JUnit5

·2023년 10월 30일
0

Java

목록 보기
4/7
post-thumbnail

📺 바다의 JUnit5 사용법

JUnit

자바 단위 테스트 프레임워크

JUnit5

모듈화가 되어 있다.
JUnut Platform 위에 Jupiter, Vintage를 올리는 구조
Platform: 테스트를 실행해주는 런처 제공, TestEngine API 제공
Jupiter : JUnit 5를 지원하는 TestEngine API 구현체
Vintage : JUnit 4와 3을 지원하는 TestEngine 구현체

스프링부트 2.2버전 이상부터는 기본적으로 추가
스프링 부트 프로젝트가 아닐 경우

<dependency>
	<groupID>org.junit.jupeter</groupId>
    <artifactId>junit-jupiter-engine</artifactId>
    <scope>test</scope>
</dependency>

위의 방법으로 의존성을 추가해주면 된다.


Annotations

@Test

테스트 메서드라는 것을 나타내는 어노테이션

생명 주기(LifeCycle) 어노테이션

@BeforeAll
해당 클래스에 위치한 모든 테스트 메서드 실행 전에 딱 한 번 실행되는 메서드
@AfterAll
해당 클래스에 위치한 모든 테스트 메서드 실행 후에 딱 한 번 실행되는 메서드

@BeforeEach
해당 클래스에 위치한 모든 테스트 메서드 실행 전에 실행되는 메서드
@AfterEach
해당 클래스에 위치한 모든 테스트 메서드 실행 후에 실행되는 메서드
매 테스트 메서드마다 새로운 클래스를 생성(new)하여 실행해 비효율적.

@Disabled

테스트를 하고 싶지 않은 클래스나 메서드에 붙이는 어노테이션

class DisabledExample {
	@Test
    @Disabled("문제가 해결될 때까지 테스트 중단")
    void test1() {
    	System.out.println("테스트");
    }
    @Test
    void test2() {
    	System.out.println("테스트2");
    }
}

test1는 실행되지 않는다!

@RepeatedTest

특정 테스트를 반복시키고 싶을 때 사용하는 어노테이션
반복 횟수와 반복 테스트 이름 설정 가능

@RepeatedTest(10)
@DisplayName("반복 테스트")
void repeatedTest(){
	...
}

@RepeatedTEst(value = 10, name = "{displayName}{currentRepetition} of {totalRepetitions})
@DisplayName("반복 테스트")
void repeatedTest2() {
	...
}

@ParameterizedTest

테스트에 여러 다른 매개변수를 대입해가며 반복 실행할 때 사용하는 어노테이션

@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(Dealer dealer){
        	player.recieveOneCard(new Card(CardNumber.ACE, CardType.CLOVER));
        	player.recieveOneCard(new Card(CardNumber.JACK, CardType.CLOVER));
            return player.findResult(dealer);
        }
        
        @DisplayName("딜러가 블랙잭이면 DRAW를 반환한다")
        @Test
        void returnDraw(){
        	final Dealer dealer = new Dealer();
        	dealer.recieveOneCard(new Card(CardNumber.ACE, CardType.HEART));
        	dealer.recieveOneCard(new Card(CardNumber.KING, CardType.HEART));
            Result expectedResult = Result.DRAW
            ..
        }
        
            

Assertions

사전적 의미 : 주장, 행사, 단정문
테스트 케이스의 수행 결과를 판별하는 메서드
모든 JUnit Juptiter Assertions는 static 메서드

assertAll

매개변수로 받는 모든 테스트코드를 한 번에 실행
오류가 나도 끝까지 실행한 뒤 한 번에 모아서 출력

@Test
public void create_study(){
	Study study = new Study();
    assertNotNull(study);
    assertEquals(Status.STARTED, study.getStatus(), "처음 상태값이 DRAFT");
    assertTrue(study.getLimit() > 0, -> "최대 인원은 0보다 커야 한다.");
}
// assertEquals에서 에러가 나면 assertTrue는 실행되지 않고 종료된다.

@Test
public void create_study(){
	Study study = new Study();
    assertAll(
    () -> assertNotNull(study);
    () -> assertEquals(Status.STARTED, study.getStatus(), "처음 상태값이 DRAFT");
    () -> assertTrue(study.getLimit() > 0, -> "최대 인원은 0보다 커야 한다.");
  );
}
// 중간에 실패한 assertion이 있어도 끝까지 실행 후 발생한 오류들을 한 번에 볼 수 있다.

assertThrows(expectedType, executable)

예외 발생을 확인하는 테스트
executable의 로직이 실행되는 도중 expectedType의 에러를 발생시키는지 확인

@Test
void exceptionThrow(){
	Exception e = assertThrows(Exception.class, () -> new Test(-10));
    //예외 발생 검증, 예외 반환 받아서 예외의 상태까지 검증할 수 있다.
    assertDoesNotThrow(()-> System.out.println("Do Something"));
    // 예외 발생하지 않음을 검증
}

assertTimeout(duration, executable)

특정 시간 안에 실행이 완료되는지 확인
duration: 원하는 시간 , executable: 테스트할 로직

@Rule
public Timeout timeout = Timeout.seconds(5);

class TimeoutExample {
	@Test
    @DisplayName("타임아웃 준수")
    void timeoutNotExceeded() {
	    assertTimeout(ofMinutes(2), () -> Thread.sleep(10));
    }
    
    @Test
    @DisplayName("타임아웃 초과")
    void timeoutExceeded() {
    	assertTimeout(ofMillis(10), () -> Thread.sleep(100));
    }
}

Assumption

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

0개의 댓글

관련 채용 정보