[24.06.12] 내일배움캠프 39일차 JAVA TIL - 단위 테스트

2024. 6. 13. 09:17T.I.L

오늘 한 일

  • 숙련주차 개인 과제 발제 듣기
  • 팀명 정하기 / 규칙 정하기
  • 단위 테스트 강의 수강

 

 


개발과 테스트의 순서

  1. Development: 개발
  2. Unit Tests (단위 테스트): 개발자 테스트
  3. QA Testing:
    • 블랙박스 테스팅
    • 주로 QA 팀이 Production 환경과 유사한 환경(Stage)에서 테스팅
  4. Production: 실 서비스 운영 환경

 

오늘 공부해 볼 목차는 단위테스트이다.

JUnit5 을 사용하여 단위 테스트를 해보자!

JUnit5 : 자바 프로그래밍 언어 용 단위 테스트 프레임워크
 

 

Before - After

@BeforeEach
void setUp() {
    System.out.println("각각의 테스트 코드가 실행되기 전에 수행");
}

@AfterEach
void tearDown() {
    System.out.println("각각의 테스트 코드가 실행된 후에 수행\n");
}

@BeforeAll
static void beforeAll() {
    System.out.println("모든 테스트 코드가 실행되기 전에 최초로 수행\n");
}

@AfterAll
static void afterAll() {
    System.out.println("모든 테스트 코드가 수행된 후 마지막으로 수행");
}

 

만일 test_1 , test_2 를 한번에 실행시킨다면

모든 테스트 코드가 실행되기 전에 최초로 수행
각각의 테스트 코드가 실행되기 전에 수행
test_1
각각의 테스트 코드가 실행된 후에 수행
각각의 테스트 코드가 실행되기 전에 수행
test_2
각각의 테스트 코드가 실행된 후에 수행
모든 테스트 코드가 수행된 후 마지막으로 수행

처럼 실행이 되는걸 확인할 수 있다.

 

@Nested 는 테스트를 클래스화 하여 그룹으로 만들어준다.

@DisplayName은 테스트의 이름을 보여주는 어노테이션이다.

@Nested
@DisplayName("주제 별로 테스트를 그룹지어서 파악하기 좋습니다.")
class Test1 {
    @Test
    @DisplayName("Test1 - test1()")
    void test1() {
        System.out.println("Test1.test1");
    }

    @Test
    @DisplayName("Test1 - test2()")
    void test2() {
        System.out.println("Test1.test2");
    }
}

@Nested
@DisplayName("Test2 다른 주제")
class Test2 {
    @Test
    @DisplayName("Test2 - test1()")
    void test1() {
        System.out.println("Test2.test1");
    }

    @Test
    @DisplayName("Test2 - test2()")
    void test2() {
        System.out.println("Test2.test2");
    }
}

 

@Order은 순서를 정해주는 어노테이션이다

반드시 @TestMethodOrder(MethodOrderer.OrderAnnotation.class) 가 같이 와줘야 한다

@Nested
@DisplayName("주제 별로 테스트를 그룹지어서 파악하기 좋습니다.")
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
class Test1 {

    @Order(1)
    @Test
    @DisplayName("Test1 클래스")
    void test() {
        System.out.println("\nTest1 클래스");
    }

    @Order(3)
    @Test
    @DisplayName("Test1 - test1()")
    void test1() {
        System.out.println("Test1.test1");
    }

    @Order(2)
    @Test
    @DisplayName("Test1 - test2()")
    void test2() {
        System.out.println("Test1.test2");
    }
}

 

 

@RepeatedTest : test계의 for 문..

@RepeatedTest(value = 5, name = "반복 테스트 {currentRepetition} / {totalRepetitions}")
void repeatTest(RepetitionInfo info) {
    System.out.println("테스트 반복 : " + info.getCurrentRepetition() + " / " + info.getTotalRepetitions());
}

 

current와 total을 확인하여 찍을 수 있다.

@RepeatedTest(value = 5, name = "반복 테스트 {currentRepetition} / {totalRepetitions}")
void repeatTest(RepetitionInfo info) {
    System.out.println("테스트 반복 : " + info.getCurrentRepetition() + " / " + info.getTotalRepetitions());
}

 

@ValueSource : List와 유사한 형태로 안의 값을 순차적으로 접근할 수도 있다.

@DisplayName("파라미터 값 활용하여 테스트 하기")
@ParameterizedTest
@ValueSource(ints = {1, 2, 3, 4, 5, 6, 7, 8, 9})
void parameterTest(int num) {
    System.out.println("5 * num = " + 5 * num);
}

 

이 외에도

해당 값과 일치하는지 

@Test
@DisplayName("assertEquals")
void test1() {
    Double result = calculator.operate(5, "/", 2);
    assertEquals(2.5, result);
}

@Test
@DisplayName("assertEquals - Supplier")
void test1_1() {
    Double result = calculator.operate(5, "/", 0);
    // 테스트 실패 시 메시지 출력 (new Supplier<String>())
    assertEquals(2.5, result, () -> "연산자 혹은 분모가 0이 아닌지 확인해보세요!");
}

@Test
@DisplayName("assertNotEquals")
void test1_2() {
    Double result = calculator.operate(5, "/", 0);
    assertNotEquals(2.5, result);
}

 

해당 값이 참인지 거짓인지

@Test
@DisplayName("assertTrue 와 assertFalse")
void test2() {
    assertTrue(calculator.validateNum(9));
    assertFalse(calculator.validateNum(0));
}

 

Null인지 아닌지

@Test
@DisplayName("assertNotNull 과 assertNull")
void test3() {
    Double result1 = calculator.operate(5, "/", 2);
    assertNotNull(result1);
    Double result2 = calculator.operate(5, "/", 0);
    assertNull(result2);
}

 

해당 오류로 던져지는지 확인할 수 있다!

@Test
@DisplayName("assertThrows")
void test4() {
    IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> calculator.operate(5, "?", 2));
    assertEquals("잘못된 연산자입니다.", exception.getMessage());
}

 

 

 


 

 

Given-When-Then 패턴은 Test Code 스타일을 표현하는 방식을 뜻합니다.

Given

  • 테스트 하고자하는 대상을 실제로 실행하기 전에 테스트에 필요한 값(상태)을 미리 선언해 둡니다.

When

  • 테스트 하고자하는 대상을 실제로 실행 시킵니다.

Then

  • 어떤 특정한 행동(테스트 대상 실행) 때문에 발생할거라고 예상되는 결과에 대해 예측하고 맞는지 확인합니다.
class CalculatorTest {

    Calculator calculator;

    @BeforeEach
    void setUp() {
        calculator = new Calculator();
    }

    @Test
    @DisplayName("계산기 연산 성공 테스트")
    void test1() {
        // given
        int num1 = 5;
        String op = "/";
        int num2 = 2;

        // when
        Double result = calculator.operate(num1, op, num2);

        // then
        assertNotNull(result);
        assertEquals(2.5, result);
    }

    @Test
    @DisplayName("계산기 연산 실패 테스트 : 분모가 0일 경우")
    void test1_1() {
        // given
        int num1 = 5;
        String op = "/";
        int num2 = 0;

        // when
        Double result = calculator.operate(num1, op, num2);

        // then
        assertNull(result);
    }

    @Test
    @DisplayName("계산기 연산 실패 테스트 : 연산자가 잘못됐을 경우")
    void test1_2() {
        // given
        int num1 = 5;
        String op = "?";
        int num2 = 2;

        // when - then
        IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> calculator.operate(5, "?", 2));
        assertEquals("잘못된 연산자입니다.", exception.getMessage());
    }
}