본문 바로가기
Test

Junit 5

by oncerun 2022. 12. 12.
반응형

 

Junit 5 설정부터 기초 내용까지 간단히 정리하자.

 

JUnit Platform : 테스팅 프레임워크를 구동하기 위한 런처와 테스트 엔진을 위한 API를 제공한다.

 

JUnit Jupiter : JUnit 5를 위한 테스트 API와 실행 엔진을 제공한다.

 

JUnit Vintage : JUnit3과 JUnit4로 작성된 테스트를 JUnit 5 플랫폼에서 실행하기 위한 모듈을 제공한다.

 

 

Gradle 인 경우 test 태스크에 JUnit 5 플랫폼을 사용하도록 설정해야 하고  testImplementation을 사용해 junit-jupiter 의존성을 추가하면 된다.

 

dependencies {
    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1'
}

test {
    useJUnitPlatform()
}

 

junit-jupiter 모듈은 JUnit 5.4 버전부터 제공한다. 

 

junit-jupiter 모듈은 junit-jupiter-api 모듈, junit-jupiter-params 모듈, junit-jupiter-engine 모듈을 포함한다. 

그렇기 때문에 이전 버전이라면 필요한 모듈을 각각 설정해주어야 한다.

 

  • Gradle 4.6 버전 부터 Junit5 플랫폼을 지원한다. 

 

 

@Test 어노테이션이 붙는 메서드는 테스트를 실행할 메서드이며 private이면 안되고 반환 값이 존재해도 안된다.

 

JUnit을 사용하게 되면 단언 메서드를 사용하기 위해 Assertions 클래스를 사용하게 된다. 

다만 유사한  org.assertj.core.api가 있으니 잘 구별해서 사용해야 한다. 

  • assertEquals(expected, actual) : 기대값과 실제값이 같은지 확인합니다.
  • assertTrue(condition) : 조건이 참인지 확인합니다.
  • assertFalse(condition) : 조건이 거짓인지 확인합니다.
  • assertNull(object) : 객체가 null 인지 확인합니다.
  • assertNotNull(object) : 객체가 null이 아닌지 확인합니다.
  • assertArrayEquals(expectedArray, actualArray) : 배열이 기댓값과 일치하는지 확인합니다.
  • fail() : 테스트를 실패 처리한다. 
  • assertIterableEquals: 두 Iterable이 동일한지 확인합니다.
  • assertLinesMatch: 두 줄이 동일한지 확인합니다.
  • assertTimeout: 지정한 시간 내에 작업이 완료되는지 확인합니다.

 

주로 타입별로 assertEquals()메서드가 존재한다. 주의할 점은 첫 번째 인자가 기대하는 값이고 두 번째 인자가 검사하려는 값이라는 것이다. 나도 처음에 헷갈려서 잘 못 사용하긴 했다..

 

 

테스트를 실패하도록 만들어야 하는 경우가 있다.  그 경우 fail()를 사용할 수 있다.

 

public class MyTest {
  @Test
  public void testSomething() {
    // some code here

    if (!condition) {
      fail("The condition was not met!");
    }

    // continue with the test
  }
}

 

혹은 try/catch문을 예상했을 때 도 사용할 수 있다.

 

다만 익셉션 발생 유무가 검증 대상이면 fail()대신 다른 메서드를 추천한다.

 

  • assertThrows 메서드는 예외가 발생하는지를 검사하고, 예외가 발생하면 그 예외를 반환합니다.
  • assertDoesNotThrow : 예외가 발생하지 않는지 검사합니다.
  • expectThrows 메서드는 예외가 발생하는지를 검사하고, 예외가 발생하면 그 예외를 반환합니다. 단, 다른 메서드와 달리 예외가 어떤 타입인지 확인하는 기능이 추가되어 있습니다.
@Test
void testException() {
  Exception exception = assertThrows(Exception.class, () -> {
    // 예외가 발생할 것으로 예상되는 코드
  });
  assertEquals("예외 메시지", exception.getMessage());
}

 

 

assert 메서드

 

assert 메서드는 실패 시 다음 코드를 실행하지 않고 바로 예외를 던지는데, 만약 여러 개의 단언 메서드가 있는 경우

일단 모든 검증을 실행하고 그 중 실패한 것을 확인하고 싶다면 assertAll()을 사용할 수 있다.

 

assertAll()은 테스트 코드 작성 시 사용되는 함수로, 전달된 모든 검증 조건이 true인지를 확인합니다.

만약 전달된 검증 조건 중 하나라도 false이면, AssertionError가 발생합니다.

이 함수는 검증 조건이 여러 개일 경우 각 검증 조건이 정상적으로 수행되었는지를 한 번에 확인할 수 있기 때문에 매우 유용합니다.

 

다음은 assertAll()을 사용한 예시입니다.

 

@Test
void testAssertAll() {
    List<String> fruits = Arrays.asList("apple", "banana", "orange");

    // assert that all elements in the list are not null
    assertAll(() -> assertNotNull(fruits.get(0)),
              () -> assertNotNull(fruits.get(1)),
              () -> assertNotNull(fruits.get(2)));

    // assert that all elements in the list are not empty
    assertAll(() -> assertFalse(fruits.get(0).isEmpty()),
              () -> assertFalse(fruits.get(1).isEmpty()),
              () -> assertFalse(fruits.get(2).isEmpty()));
}

 

이는 Executable 목록을 가변 인자로 전달받아 각 Executable을 실행하는데, 실패하는 코드가 있으면 목록에 모아서 에러 메시지로 보여준다.

 

 

 

테스트 라이프 사이클

 

  • 테스트 메서드를 포함한 객체 생성
  • @BeforeEach 메서드 실행
  • @Test 메서드 실행
  • @AfterEach 메서드 실행

 

 

@BeforeEach에 대한 예시는 다음과 같습니다.

 

class TestExample {
    private List<String> fruits;

    @BeforeEach
    void setUp() {
        fruits = Arrays.asList("apple", "banana", "orange");
    }

    @Test
    void testFruitsNotNull() {
        // assert that all elements in the list are not null
        assertAll(() -> assertNotNull(fruits.get(0)),
                  () -> assertNotNull(fruits.get(1)),
                  () -> assertNotNull(fruits.get(2)));
    }

    @Test
    void testFruitsNotEmpty() {
        // assert that all elements in the list are not empty
        assertAll(() -> assertFalse(fruits.get(0).isEmpty()),
                  () -> assertFalse(fruits.get(1).isEmpty()),
                  () -> assertFalse(fruits.get(2).isEmpty()));
    }
}

 

@Test 메서드가 종료된 이후 실행되는 @BeforeEach 예제 코드는 다음과 같습니다.

 

public class MyTestClass {
    // Some test methods

    @AfterEach
    public void tearDown() {
        // Perform cleanup tasks or reset the system state here
    }
}

 

재밌는 점은 @Test 메서드를 실행 할 때마다 객체를 새로 생성하고 테스트 메서드를 실행한다는 점입니다.

 

만약 모든 테스트 메서드가 실행되기 전 딱 한번 실행되어야 하는 코드가 있다면

 

@BeforeAll 애너테이션을 사용할 수 있습니다.

public class MyTestClass {
    @BeforeAll
    public static void setUp() {
        // Perform tasks that need to be done once before all tests are run
    }

    // Some test methods
}

 

반대로 @AfterAll() 메서드는 클래스의 모든 테스트 메서드를 실행한 뒤에 실행됩니다. 

public class MyTestClass {
    // Some test methods

    @AfterAll
    public static void tearDown() {
        // Perform tasks that need to be done once after all tests are run
    }
}

이 두 개의 메서드는 정적 메서드에 적용합니다.

 

 

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

 

테스트 메서드는 특정 순서대로 실행된다는 가정 하여 테스트 메서드를 가정하면 안 됩니다. 그 순서는 버전에 따라 달라질 수 있습니다. 순서에 따라 테스트가 실패한다는 것은 상당한 의문점을 가져야 하는 부분입니다.,

 

기억해야 할 점은 각 테스트 메서드는 서로 독립적으로 동작해야 한다는 것입니다. 

 

이를 위해 서로 필드를 공유한다거나 실행 순서를 가정하고 테스트를 작성하지 말아야 합니다.

 

(Junit은 실행 순서를 지정하는 방법도 제공합니다.)

import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;

@TestMethodOrder(MethodOrderer.Alphanumeric.class)
public class MyTestClass {
    @Test
    public void testA() {
        // Test code
    }

    @Test
    public void testB() {
        // Test code
    }

    @Test
    public void testC() {
        // Test code
    }
}

 

 

모든 테스트 실행

 

코드를 원격 리포지토리에 푸시하거나 빌드 후 배포하기 전에는 모든 테스트를 실행해서 문제가 되는 부분이 없는지 확인해야 합니다. 

 

이때 사용하는 방법은 간단하다.

 

  • mvn test
  • gradle test ( gradlew test)

 

하지만 대부분 빌드 툴에서는 빌드하기 전에 test 태스크를 먼저 진행하기 때문에 mvc pacakge, gradle build 하여도 테스트가 진행된다.

 

 

반응형

'Test' 카테고리의 다른 글

테스트 가능한 설계  (2) 2022.12.15
Spy  (0) 2022.12.14
테스트 코드에 작성 순서가 있다고?  (0) 2022.12.10
TDD init  (0) 2022.12.10
TDD 시작  (0) 2022.12.10

댓글