Spring/팀스파르타

43. 단위 테스트

열심히 해 2024. 12. 6. 14:55

 

 

Unit Tests(단위 테스트): 개발자 테스트

  • QA Testing:
    • 블랙박스 테스팅
    • 주로 QA 팀이 Production 환경-실 서비스 운영 환경과 유사한 환경(Stage)에서 테스팅
  • 테스트 코드를 작성한다면 프로그램의 버그를 사전에 발견하여 기하급수적인 비용의 증가 가능성을 사전에 방지할 수 있습니다.
  • 개발자는 단위 테스트를 작성하여 프로그램을 테스트 할 수 있습니다.
    • 단위 테스트는 작은 단위로 쪼개서 각 단위가 정확하게 동작하는지를 검사하는 테스트 기법입니다.
    • 단위 테스트는 빠르게 작성할 수 있고 문제 발생 시 어느 부분이 잘못 되었는지를 빠르고 정확하게 확인할 수 있다는 장점이 있습니다.
  • 테스트 코드 전체를 한 번에 실행시킬 때 항상 코드가 작성된 순서대로 실행되지 않습니다.
  • 테스트 시 main 메서드가 실행되는 것이 아니며, test 만을 위한 platfrom이 따로 존재합니다.




 

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

 

@BeforeEach, @AfterEach, @BeforeAll, @AfterAll

 

public class BeforeAfterTest {

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

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

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

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

    @Test
    void test1() {
        System.out.println("테스트 작성 1");
    }

    @Test
    void test2() {
        System.out.println("테스트 작성 2");
    }
}

 

 

@DisplayName, @Nested, @TestMethodOrder - @Order

public class CustomTest {
    @Test
    @DisplayName("테스트의 내용을 알아볼 수 있게 네이밍")
    void test1() {
        System.out.println("테스트 내용 빠르게 파악");
    }

    @Nested
    @DisplayName("Nested 1번, 주제 별로 테스트를 그룹지어서 파악하기 좋습니다.")
    @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");
        }
    }

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

 

 

@RepeatedTest, @ParameterizedTest

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

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

 

 

Assertions.assertEquals(expected, actual), Assertions.assertTrue(boolean),
Assertions.assertNotNull(actual), Assertions.assertThrows(expectedType, executable)

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

    @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));
    }
    @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());
    }
}
public class Calculator {
    public Double operate(double num1, String op, double num2) {
        switch (op) {
            case "*":
                return num1 * num2;
            case "/":
                if (validateNum(num2)) {
                    return num1 / num2;
                } else {
                    return null;
                }
            case "+":
                return num1 + num2;
            case "-":
                return num1 - num2;
            default:
                throw new IllegalArgumentException("잘못된 연산자입니다.");
        }
    }

    public boolean validateNum(double num) {
        if (num == 0) {
            return false;
        } else {
            return true;
        }
    }
}

 

 


Given-When-Then 패턴

  • 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());
    }
}

 

'Spring > 팀스파르타' 카테고리의 다른 글

47. 소셜 로그인 - OAuth  (0) 2024.12.06
44. Mockito  (0) 2024.12.06
42. 고아 Entity 삭제  (0) 2024.12.06
41. 영속성 전이  (0) 2024.12.06
40. 즉시 로딩, 지연 로딩과 영속성 컨텍스트  (0) 2024.12.06