[JUnit] 란 무엇일까?

최동근·2022년 12월 16일
0

TDD

목록 보기
2/4
post-thumbnail

🧪 단위 테스트(Unit Test)란 ?

JUnit은 Java 에서 독립된 단위테스트를 지원해주는 프레임워크이기 때문에
단위 테스트에 대해 먼저 알아보겠습니다 💡

테스트 대상 단위가 엄격하게 정해져있지는 않지만, 일반적으로 클래스 또는 메소드 수준으로 정해집니다. 단위의 크기가 작을수록 단위의 복잡성이 낮아지고, 실행하려는 기능을 표현하기 더 쉬워지는게 당연하겠죠?

단위테스트란 특정 소스코드의 모듈이 의도한 대로 잘 작동하는지 검증하는 테스트입니다.
즉, 함수 및 메소드에 대한 테스트를 하는 작업으로써, Spring에서 단위테스트는 Spring Container에 올라와 있는 Bean 을 테스트 하는 것입니다.

💡 단위 테스트의 장점

단위 테스트는 해당 부분만 독립적으로 테스트하기 때문에 어떤 코드를 리팩토링하여도 빠르게 문제 여부를 파악할 수 있습니다.
또한, 개발자는 작성한 테스트 코드를 수시로 빠르게 돌리면서 문제점을 파악할 수 있습니다.
단위 테스트를 진행하는 동안에는 실제 어플리케이션을 동작시킬 때와 마찬가지로 Bean을 테스트 할 수 있습니다 🧑🏼‍💻

🧪 JUnit 이란?

  • 자바 개발자가 가장 많이 사용하는 테스팅 기반 프레임워크 입니다.
  • 현재 가장 많이 사용되고 있는 버전은 JUnit 5 입니다.
  • 기본 Junit 4에 비해 JUnit 5는 3가지 모듈로 구성됩니다.
  • JUnit5는 테스트 작성자를 위한 API 모듈과 테스트 실행을 위한 API가 분리되어 있습니다.
  • 자바 8 이상을 필요로 합니다.

🧪 JUnit 구조

해당 구조에 대해 간단히 알아보겠습니다 💡
기존 버전인 JUnit4에서는 vintage-engine 모듈을 사용해서 테스팅을 진행했습니다.
그러나 이와 달리, JUnit5는 크게 3가지 모듈로 구성됩니다.

JUnit5 = JUnit Platform + JUnit Jupiter + JUnit Vintage

JUnit Jupiter API : JUnit5에 새로 추가된 테스트 코드용 API

  • JUnit Platform : 테스트를 발견하고 테스트 계획을 생성하는 TestEngine 인터페이스를 정의 합니다.
    즉, 기초적인 역할을 수행하며 TestEngine API 를 제공합니다.

  • JUnit Jupiter : JUnit5에서 테스트 및 Extension을 작성하기 위한 새로운 프로그래밍 모델과 확장 모델의 조합이며, TestEngine API 구현체 입니다.
    JUpiter 는 테스트 코드 작성에 필요한 junit-jupiter-api 모듈과 테스트 실행을 위한 junit-jupiter-engine 모듈로 분리되어 있다.

    • JUnit Jupiter = junit-jupiter-api + junit-jupiter-engine
  • JUnit Vintage : JUnit3, JUnit4 를 실행할 수 있는 TestEngine 입니다. 하위 호환성을 위해서 존재합니다.
    TestEngine의 실제 구현체는 별도 모듈로 존재하는데, 이 중 하나가 jupiter-engine 입니다.

🧪 테스트의 Class & Method 어노테이션

Test 역시 자바의 객체로 구성됩니다.
따라서 클래스로 구현을 하기 때문에 Class + Method 형태로 구성됩니다.

  • Class : 최상위 클래스, static member class, @Nested class 중 적어도 하나의 메소드를 포함하는 클래스

    • Test Class 는 absctract 이면 안되며, 기본 생성자를 포함해야 합니다 💪
  • Test Method : @Test, @RepeatedTest, @TestFactory 등의 어노테이션이 선언되어 있는 메서드로 실제 테스트하고자 하는 내용을 넣습니다.

    • @Test
      : 해당 메소드가 테스트 메소드임을 나타냅니다.

    • @ParametherizedTest
      : 해당 메소드가 매개변수가 있는 테스트임을 나타냅니다.

    • @RepeatedTest
      : 반복되는 테스트 메소드를 나타냅니다. -> @RepeatedTest(3)

  • LifeCycle Method : @BeforeAll, @BeforeEach, @AfterAll, @AfterEach 가 선언되어 있는 메서드로 Test 의 Life Cycle에 따라 실행되는 메소드 입니다.
    • @BeforeAll
      : 테스트 Class 기준으로 테스트 메서드들이 실행되기 전 실행
      즉, 테스트 시작 시점에 딱 한번 실행(static 메소드만 가능)
    • @BeforeEach
      : 각 테스트 메서드가 실행되기 전 실행
      일반적으로 Mock 데이터를 미리 세팅하기 위해 사용
    • @AfterAll
      : 테스트 Class 기준을 테스트 메서드들이 실행된 후 실행
      즉, 테스트 종료 시점에 딱 한번 실행(static 메소드만 가능)
    • @AfterEach
      : 각 테스트 메서드가 실행되기 전 실행

🔄 전체적인 Life Cycle
BeforeAll -> BeforeEach -> Test -> AfterEach -> AfterAll

🧪 기타 어노테이션

  • @Tag : 테스트 클래스, 메소드에 테스트 구분을 태킹하기 위해 사용
  • @Disabled : 해당 클래스나 테스트를 사용하지 않음
  • @Timeout : 테스트 실행 시간 선언, 초과되면 실패하도록 설정
  • @ExtendWith : 확장을 선언적으로 등록할 때 사용
  • @TempDir : 필드 주입 혹은 매개변수 주입을 통해 임시 디렉토리 제공
  • @DispayName : 테스트 메소드 이름 설정(기본값 -> 메소드 이름)

🧪 Assertions

org.junit.jupiter.api.Assertions 패키지를 통해 사용 가능

  • assertEquals

    • 두 값을 비교하여 일치 여부 판단
@Test
@DisplayName("assertEquals Test")
public void test () { 

	String expected = "2023/01/07";
    String actual = "2023/01/07";
    
    assertEqauls(expected, actual); // Test Succeed!
    
}
  • assertArrayEqauls

    • 두 배열을 비교하여 일치 여부 판단
    • 동등성을 비교하며, 두 배열이 null 이여도 동일한 것으로 판단
@Test
@DisplayName("assertArrayEquals Test")
public void test02() {
    
    char[] expected = {'J','u','n','i','t'};
    char[] actual = "Junit".toCharArray();

    System.out.println(expected == actual); // 동일성 측면에서는 false

     assertArrayEquals(expected, actual); // 동등성 측면에서는 true
 }
  • assertNotNull & assertNull

    • 객체의 null 여부 확인
@Test
@DisplayName("assertNotNull & assertNull Test")
public void test03() {

	Object obj01 = null;
    Object obj02 = new String("오늘은 2023/01/07");

	assertNull(obj01);
    assertNotNull(obj02);

}
  • assertTrue & assertFalse

    • 특정 조건이 true인지 false 인지 판단
 @Test
 @DisplayName("assertTrue & assertFalse Test")
    public void test04() {

    assertTrue(5 > 4,"5 is greater than 4");
    assertFalse(5 > 6, " 5 is not greater than 6"); // Message 설정도 가능

}
  • fail

    • AssertionFailedError를 발생시키는 테스트에 실패
    • 실제 예외가 발생했는지 확인하거나, 개발 중에 테스트를 실패하게 만들고 싶을 때 사용
@Test
public void test() {
    try {
        methodThatShouldThrowException();
        fail("Exception not thrown");
    } catch (UnsupportedOperationException e) {
        assertEquals("Operation Not Supported", e.getMessage());
    }
}
  • assertThat

    • 첫번째 파라미터에 비교대상 값, 두번째 파라미터에 비교 로직이 담긴 Matcher 를 받음
@Test
    @DisplayName("assertThat Test")
    public void test05(){

        // hamcrest 라이브러리 -> JUnit 에 사용되는 Matcher 라이브러리
        // spring-boot-starter-test 기본적으로 포함
        
        assertThat("Sample String", is(not(startsWith("Test"))));
        
        // when
        // then
     }
  • assertThrows

    • 사용자가 선택한 예외가 발생하는지 여부를 판단
@Test
     @DisplayName("assertThrows Test")
     public void test06(){
         // given

         IllegalArgumentException exception =
                 assertThrows(IllegalArgumentException.class, () -> {
                     DoSomething.func();
                 });
         // when
         // then
      }
  • assertAll

    • 해당하는 모든 assertion 체크
    • 하나라도 실패시 그 밑의 코드는 더 이상 진행하지 않음
@Test
@DisplayName("assertAll Test")
void assertAllTest(){
     // given
     String s1 = "Hello";
     String s2 = "Hello";

     assertAll(

                () -> assertNotNull(s1),
                () -> assertEquals(s2,s1),
                () -> assertTrue(s1.length() > 2)
    );
        // when
        // then
}
  • assertTimwout

    • 지정한 시간 내에 assertion 실행이 완료되는지 확인
@Test
@DisplayName("assertTimeout Test")
void assertTimeoutTest(){

assertTimeout(Duration.ofMillis(1000), () -> {

          new String("good morning");
          Thread.sleep(300);
          
        });
    }
}

🧪 AssertJ 라이브러리

AssertJ 는 JUnit Assertions을 사용함에 있어 더 우아한(?) 테스트 작성을 도와주는 라이브러리 입니다 👍
spring-boot-starter-test 에 포함되어 있습니다 💪

Hamcrest와 비슷한 기능의 메소드를 제공합니다.
또한 핵심 메소드는 assertThat 1️⃣ 개 이며 메소드 체이닝 방식을 사용합니다.
Hamcrest 프레임워크사용하기_1 을 참조해주세요.

💡 Object Assertions

두 객체의 동일성이나 객체의 필드를 조사하기 위해 사용

Example

public class Dog {

	private String name;
    private Double weight;
    
}

@Test
public class DogTest() { 

	Dog dog01 = new Dog("진돗개", 3.45);
    Dog dog02 = new Dog("시추" , 1.23);
    
    assertThat(dog01).isEqualTo(dog02);

}

isEqualsTo() 는 객체의 동일성을 비교하므로 해당 테스트는 fail입니다 🥲
동등성을 비교하기 위해서는 isEqualToComparingFieldByFieldRecursively() 을 사용합니다.

💡 Boolean Assertions

두 객체의 Boolean 값 비교를 위해 사용

  • isTrue()
  • isFalse()

Example

assertThat("".isEmpty()).isTrue(); // true

💡 Iterable/Array Assertions

Iterable/Array 에 특정 요소가 존재하는지 확인 등의 다양한 목적으로 사용

List<String> list = Arrays.asList("1","2","3");

assertThat(list).contains("1"); // 포함되었는지 확인

assertThat(list).isNotEmpty(); // 비어있는지 확인

assertThat(list).startsWith("1"); // 해당 요소로 시작하는지 확인

assertThat(list).doesNotContainNull() // null 포함 하지 않는지 확인
 

이외에도 AssertJ 를 이용해서 다양한 Case를 직관적으로 테스트 할 수 있습니다 💪

🧪 마무리

JUnit5는 자바를 이용하는 개발자라면 당연히 숙지해야할 테스팅 기법입니다 📚
특히 요즘 개발 문화에 TDD 는 중요한 요소로 자리 잡았기에 더욱 필수적으로 익혀야 한다고 생각합니다 🙆🏻

참고

완벽정리! junit5로 예외 테스트 하는 법
[Junit5] JUnit 5 구성, 어노테이션 적용

profile
비즈니스가치를추구하는개발자

0개의 댓글