[JUnit5] 단위테스트 생성하기(토비의 스프링)

smiler·2023년 7월 23일
post-thumbnail

토비의 스프링 공부중에 2장 테스트에서 JUnit을 사용해서 단위테스트를 하길래 개인적으로 정리해봅니다.

토비의 스프링에서는 테스트 도구로 이런것들이 있다 정도로만 사용하는데, 깊게 공부하려면 테스트 코드 작성개발보다는 테스트 모델을 설계하는 관점에서 해야할 것 같습니다.

1. 라이브러리 추가

https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-engine
https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-params

-- 버전은 그때그때 괜찬아보이는걸로 사용합니다.
-- 보통은 pom.xml에서 버전관리를 따로 하도록 작성하지만 ..
-- MAVEN
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-engine</artifactId>
    <version>5.8.2</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-params</artifactId>
    <version>5.9.3</version>
    <scope>test</scope>
</dependency>


-- Gradle
testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.8.2'
testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-params', version: '5.9.3'

2. Test Class 생성

테스트 코드를 작성할 클래스를 생성합니다.
직접 작성해도 되지만 IDE에서 대부분 자동생성을 지원해주기 때문에 IDE의 도움을 받겠습니다. 저는 Intellij 기준입니다.

보통 이름은 테스트 클래스명 + Test로 생성합니다
(ex. Hamburger.java -> HamburgerTest.java)

  1. 테스트 생성을 하고싶은 클래스파일에 들어가서 클래스명 우클릭
  2. Go To 선택
  3. Test 선택
  4. Create new Test선택

  1. Testing library 에 여러개 있지만 JUnit5로 선택
  2. 이것저것 있지만 아래 테스트하고 싶은 메소드 체크 후 OK 클릭
    - 잘못선택해도 생성되는 Class File에서 변경 가능합니다.

4. Test Code 작성

- Annotation

@Test : 테스트 Method 선언
@ParameterizedTest : 매개변수를 받는 테스트 작성
@RepeatedTest : 반복 테스트 작성
@TestFactory : 동적 테스트 작성
@TestInstance : 테스트 클래스의 생명주기 설정
@TestMethodOrder : 테스트 메소드 실행 순서 구성에 사용
@DisplayName : 테스트 클래스 or 메소드의 사용자 정의 이름 선언
@DisplayNameGeneration : 이름 생성기 선언
@BeforeEach : 모든 테스트 실행 전에 실행할 테스트
@AfterEach : 모든 테스트 실행 후에 실행할 테스트
@BeforeAll : 현재 클래스 실행 전 제일 먼저 실행할 테스트
@AfterAll : 현재 클래스 종료 직후 실행할 테스트
@Nested : (정적이 아닌) 중첩 테스트 클래스
@Tag : 클래스 or 메소드 레벨에서 태그 선언
@Disabled : 이 클래스나 테스트를 사용하지 않음
@Timeout : 테스트 실행 시간 선언, 초과되면 실패하도록 설정
@ExtendWith : 확장을 선언적으로 등록할 때 사용
@RegisterExtension : 필드를 통해 프로그래밍 방식으로 확장을 등록할 때 사용
@TempDir : 필드 주입 or 매개변수 주입을 통해 임시 디렉토리 제공

- Assertions

https://www.baeldung.com/junit-assertions

1. assertArrayEquals

  • 같은 값인지 확인합니다.
@Test
void whenAssertingEquality_thenEqual() {
    float square = 2 * 2;
    float rectangle = 2 * 2;

    assertEquals(square, rectangle);
}
  • 아래와 같이 불일치할경우 출력을 바꿀수 있습니다.
assertEquals("failure - strings are not equal", expected, actual);

2. assertArrayEquals

  • 같은 Array인지 확인합니다.
@Test
public void whenAssertingArraysEquality_thenEqual() {
    char[] expected = { 'J', 'u', 'p', 'i', 't', 'e', 'r' };
    char[] actual = "Jupiter".toCharArray();

    assertArrayEquals(expected, actual, "Arrays should be equal");
}

3. assertNull & assertNotNull

  • Object가 null 인지 확인합니다.
@Test
void whenAssertingNotNull_thenTrue() {
    Object dog = new Object();

    assertNotNull(dog, () -> "The dog should not be null");
}

4. assertSame & assertNotSame

  • Object가 같은 Object를 참조하는지 확인합니다.
@Test
void whenAssertingSameObject_thenSuccessfull() {
    String language = "Java";
    Optional<String> optional = Optional.of(language);

    assertSame(language, optional.get());
}

5. assertTrue & assertFalse

  • 조건의 true / false를 확인합니다.
@Test
void whenAssertingConditions_thenVerified() {
    assertTrue(5 > 4, "5 is greater the 4");
    assertTrue(null == null, "null is equal to null");
}

6. fail

  • 예외를 발생시키거나, 확인할 때 사용합니다.
@Test
public void test() {
    try {
        methodThatShouldThrowException();
        fail("Exception not thrown");
    } catch (UnsupportedOperationException e) {
        assertEquals("Operation Not Supported", e.getMessage());
    }
}

7. assertAll

  • 인자 내 모든 Assertion이 실행되고, 결과를 하나로 출력해주는 그룹화된 Assertion 입니다.
@Test
public void test() {
    assertAll(
      "heading",
      () -> assertEquals(4, 2 * 2, "4 is 2 times 2"),
      () -> assertEquals("java", "JAVA".toLowerCase()),
      () -> assertEquals(null, null, "null is equal to null")
    );
}

8. assertIterableEquals

  • 두 Iterable간 같은 결과인지 확인합니다.
  • Array 순서가 일치해야 합니다.
@Test
public void test() {
    Iterable<String> al = new ArrayList<>(asList("Java", "Junit", "Test"));
    Iterable<String> ll = new LinkedList<>(asList("Java", "Junit", "Test"));

    assertIterableEquals(al, ll);
}

9. assertLinesMatch

  • 일치하는 항목이 있는지 확인합니다.
@Test
public void test() {
    List<String> expected = asList("Java", "\\d+", "JUnit");
    List<String> actual = asList("Java", "11", "JUnit");

    assertLinesMatch(expected, actual);
}

10. assertNotEquals

  • 결과값이 다른지 확인합니다.
@Test
public void test() {
    Integer value = 5; // result of an algorithm
    
    assertNotEquals(0, value, "The result cannot be 0");
}

11. assertThrows

  • 특정 예외를 확인합니다.
@Test
void test() {
    Throwable exception = assertThrows(
      IllegalArgumentException.class, 
      () -> {
          throw new IllegalArgumentException("Exception message");
      }
    );
    assertEquals("Exception message", exception.getMessage());
}

@Test
void test() {
	Exception exception = assertThrows(ArithmeticException.class, () ->
	calculator.divide(1, 0));
	assertEquals("/ by zero", exception.getMessage());
}

11. assertTimeout & assertTimeoutPreemptively

  • 일정 시간안에 작동하는지 확인합니다.
  • assertTimeoutPreemptively : 일정 시간안에 끝내지 못하면 종료합니다.
@Test
public void test() {
    assertTimeout(
      ofSeconds(2), 
      () -> {
        // code that requires less then 2 minutes to execute
        Thread.sleep(1000);
      }
    );
}

0개의 댓글