테스트 코드 주도 개발 시작하기 5장 정리

이채은·2023년 12월 13일
0
post-thumbnail

JUnit 5 모듈 구성

크게 3개의 요소로 구성되어 있다.

  • JUnit 플랫폼
    • 테스팅 프레임워크를 구동하기 위한 런처와 테스트 엔진을 위한 API를 제공한다.
  • JUnit 주피터
    • JUnit 5를 위한 테스트 API와 실행 엔진을 제공한다.
  • JUnit 빈티지
    • JUnit 3과 4로 작성된 테스트를 JUnit 5 플랫폼에서 실행하기 위한 모듈을 제공한다.

JUnit 5 build.gradle 설정

dependencies {
	testImplementation('org.junit.jupiter:junit-jupiter:5.5.0')
}
test {
	useJUnitPlatform()
}
  • junit-jupiter 의존 추가
  • test 태스크 JUnit 5 플랫폼 사용하도록 설정

Test 어노테이션과 테스트 메서드

JUnit을 이용해서 테스트 코드 만드는 법

  • 테스트로 사용할 클래스를 만들고 @Test 어노테이션을 메서드에 붙이기만 하면 된다.
import org.junit.jupiter.api.Test;
    
import static org.junit.jupiter.api.Assertions.assertEquals;
    
public class SumTest {
    
    @Test
    void sum() {
        int result = 2 + 3;
        assertEquals(5, result);
    }
}
  • 테스트 클래스의 이름은 보통 다른 클래스와 구분을 쉽게 하기 위해 ‘Test’를 접미사로 붙인다.
  • @Test 어노테이션을 붙인 메서드는 private이면 안 된다.

주요 단언 메서드

Assertions 클래스는 여러 단언 메서드를 제공한다.

메서드설명
assertEquals(expected, actual)실제 값(actual)이 기대하는 값(expected)과 같은지 검사한다.
assertNotEquals(unexpected, actual)실제 값(actual)이 특정 값(unexpected)과 같지 않은지 검사한다.
assertSame (Object expected, Object actual)두 객체가 동일한 객체인지 검사한다.
assertNotSame (Object unexpected, Object actual)두 객체가 동일하지 않은 객체인지 검사한다.
assertTrue (boolean condition)값이 true인지 검사한다.
assertFalse (boolean condition)값이 false인지 검사한다.
assertNull (Object actual)값이 null인지 검사한다.
assertNotNull (Object actual)값이 null이 아닌지 검사한다.
fail()테스트를 실패 처리한다.

assertEquals() 메서드

  • 주요 타입별로 존재한다.
    • int 타입을 위한 assertEquals() 메서드, Long 타입을 위한 assertEquals() 메서드 등
  • assertEquals(expected, actual)
    • 첫 번째 인자가 기대하는 값이고 두 번째 인자가 검사하려는 값이다.

fail() 메서드

  • 테스트에 실패했음을 알리고 싶을 때 사용한다.
  • e.g) 파라미터 값이 null이면 IllegalArgumentException이 발생하도록 구현한 상태에서 null을 전달했는데, 익셉션이 발생하지 않으면 테스트에 실패했다고 볼 수 있다.
try {
    AuthService authService = new AuthService();
    authService.authenticate(null, null);
    fail(); // 이 지점에 다다르면 fail()메서드는 테스트 실패 에러를 발생
} catch(IllegalArgumentException e) {
}

but, 익셉션 발생 유무가 검증 대상이라면 fail() 메서드를 사용하는 것보다 따로 제공하는 익셉션 발생 유무 검사 메서드를 사용하는 것이 더욱 명시적이다.

assertThrows()

assertThrows() 을 이용해서 지정한 익셉션이 발생하는지 검사하는 코드

assertThrows(IllegalArgumentException.class,
	() -> {
		AuthService authService = new AuthService();
		authService.authenticate(null, null);
	});
  • assertThrows() 메서드는 발생한 익셉션 객체를 리턴한다.
  • 발생한 익셉션을 이용해서 추가로 검증이 필요하다면 assertThrows() 메서드가 리턴한 익셉션 객체를 사용하면 된다.
assertThrows(IllegalArgumentException.class,
    () -> {
    	AuthService authService = new AuthService();
    	authService.authenticate(null, null);
    });
assertTure(thrown.getMessage().contains("id"));

assertAll()

  • assert 메서드는 실패하면 다음 코드를 실행하지 않고 바로 익셉션을 발생한다.
  • 하지만, 경우에 따라 일단 모든 검증을 실행하고 그중에 실패한 것이 있는지 확인하고 싶을 때, assertAll() 메서드를 사용할 수 있다.
assertAll(
		() -> assertEquals(3, 5/2),
		() -> assertEquals(4, 2*2),
		() -> assertEquals(6, 11/2)
);
  • assertAll() 메서드는 실행 결과로 검증에 실패한 코드가 있으면 그 목록을 모아서 에러 메세지로 보여준다.


테스트 라이프사이클

💡 JUnit의 테스트 메서드 코드 실행 순서
  1. 테스트 메서드를 포함한 객체 생성
  2. (존재하면) @BeforeEach 어노테이션이 붙은 메서드 실행
  3. @Test 어노테이션이 붙은 메서드 실행
  4. (존재하면) @AfterEach 어노테이션이 붙은 메서드 실행
  • 간단한 테스트 코드 예
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

public class LifecycleTest {
    public LifecycleTest() {
        System.out.println("new LifecycleTest");
    }
    
    @BeforeEach
    void setUp() {
        System.out.println("setUp");
    }
    
    @Test
    void a() {
        System.out.println("A");
    }
    
    @Test
    void b() {
        System.out.println("B");
    }
    
    @AfterEach
    void tearDown() {
        System.out.println("tearDown");
    }
}
  • 실행 결과

~ 결과를 보고 알 수 있는 사실 ~

  • @Test 메서드를 실행할 때마다 객체를 새로 생성한다.
  • 테스트 메서드를 실행 하기 전과 후에 @BeforeEach 어노테이션과 @AfterEach 어노테이션을 붙인 메서드를 실행한다.

@BeforeEach 어노테이션

  • 테스트를 실행하는데 필요한 준비 작업을 할 때 사용한다.
    • 테스트에서 사용할 임시 파일, 객체 등을 생성

@AfterEach 어노테이션

  • 테스트를 실행한 후에 정리할 것이 있을 때 사용한다.
    • 테스트에서 사용한 임시 파일을 삭제해야 할 때 등

💡 @BeforeEach, @AfterEach 어노테이션을 붙인 메서드는 @Test 어노테이션과 마찬가지로 private면 안 된다.

@BeforeAll 어노테이션

  • 한 클래스의 모든 테스트 메서드가 실행되기 전에 특정 작업을 수행해야 할 때 사용한다.
  • 정적 메서드에 붙여서 사용하는데, 이 메서드는 클래스의 모든 테스트 메서드를 실행하기 전에 한 번 실행된다.

@AfterAll 어노테이션

  • 클래스의 모든 테스트 메서드를 실행한 뒤에 실행된다.
  • 정적 메서드에 적용한다.

테스트 메서드 간 실행 순서 의존과 필드 공유하지 않기

  • 테스트 메서드가 특정 순서대로 실행된다는 가정하에 테스트 메서드를 작성하면 안 된다.
  • 테스트 메서드를 작성한 순서에 상관 없이 테스트 순서가 결정된다.
    • 메서드간의 순서가 의존되어 있다면 순서가 달라졌을 때 테스트는 실패한다.

즉, 각 테스트 메서드는 서로 독립적으로 동작해야 하고, 한 테스트 메서드의 결과에 따라 다른 테스트 메서드의 실행 결과가 달라지면 안 된다.

→ 테스트 메서드가 서로 필드를 공유하거나 실행 순서를 가정하고 테스트를 작성하지 말아야 한다!

@DisplayName 어노테이션

자바는 메서드 이름에 공백이나 특수 문자를 사용할 수 없기 때문에 메서드 이름만으로 테스트 내용을 설명하기가 부족할 수 있기에 @DisplayName 어노테이션을 사용해서 테스트에 표시 이름을 붙인다.

@DisplayName("라이프사이클 테스트")
public class LifecycleTest {
    @DisplayName("에이!")
    @Test
    void a() {
        System.out.println("A");
    }

    @Test
    void b() {
        System.out.println("B");
    }
}

@Disabled 어노테이션

  • 특정 테스트를 실행하지 않고 싶을 때 사용한다.
  • @Disabled 어노테이션이 붙은 클래스나 메서드는 테스트 실행 대상에서 제외한다.
  • 아직 테스트 코드가 완성되지 않았거나 잠시 동안 테스트를 실행하지 말아야 할 때 이 어노테이션을 사용한다.

모든 테스트 실행하기

src/test/java 폴더에서 테스트를 실행하면 된다.

0개의 댓글