테스트 코드는 왜 작성해야 하나?

Development: 개발
Unit Tests (단위 테스트): 개발자 테스트
QA Testing: 블랙박스 테스팅, 주로 QA팀이 Production 환경과 유사한 환경에서 테스팅
Production: 실 서비스 운영 환경
단위 테스트
단위 테스트는 작은 단위로 쪼개서 각 단위가 정확하게 동작하는지를 검사하는 테스트 기법이다.
테스트 애노테이션

출력 값:

테스트 꾸미기
@DisplayName: 테스트의 내용을 한눈에 알아볼 수 있게 네이밍 해줄 수 있다.
@Test
@DisplayName("테스트의 내용을 한눈에 알아볼 수 있게 네이밍 해줄 수 있습니다.")
void test1() {
System.out.println("테스트의 수행 내용들을 빠르게 파악할 수 있습니다.");
}
@Nested: 주제 별로 테스트를 그룹지어서 파악하기 좋다.
@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: 해당 메서드를 반복할 수 있다. name 속성을 사용하여 네이밍할 수 있다. RepetitionInfo 값을
파라미터로 받아서 현재 반복 횟수와 총 횟수 값을 확인할 수 있다.
@RepeatedTest(value = 5, name = "반복 테스트 {currentRepetition} / {totalRepetitions}")
void repeatTest(RepetitionInfo info) {
System.out.println("테스트 반복 : " + info.getCurrentRepetition() + " / " + info.getTotalRepetitions());
}

@ParameterizedTest: 파라미터를 받아 테스트할 수 있는 메서드를 만들 수 있다.
@DisplayName("파라미터 값 활용하여 테스트 하기")
@ParameterizedTest
@ValueSource(ints = {1, 2, 3, 4, 5, 6, 7, 8, 9})
void parameterTest(int num) {
System.out.println("5 * num = " + 5 * num);
}

반환 값 검증 테스트
assertEquals():
첫 번째 파라미터에 예상값을 넣고 두 번째 파라미터에 테스트 결과값(실제값)을 넣는다. 예상값과 실제값이 다르면 테스트가 실패한다. 3번째 파라미터 값에 람다식으로 메시지를 넣어두면 테스트 실패 시 해당 메시지가 출력된다
@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);
}
assertTrue(): 해당 파라미터 값이 true인지 확인한다.
@Test
@DisplayName("assertTrue 와 assertFalse")
void test2() {
assertTrue(calculator.validateNum(9));
assertFalse(calculator.validateNum(0));
}
assertNotNull(): 해당 파라미터 값이 null이 아님을 확인한다.
@Test
@DisplayName("assertNotNull 과 assertNull")
void test3() {
Double result1 = calculator.operate(5, "/", 2);
assertNotNull(result1);
Double result2 = calculator.operate(5, "/", 0);
assertNull(result2);
}
assertThrows():
첫 번째 파라미터에 예상하는 Exception 클래스 타입을 넣고 두 번째 파라미터에 실행 코드를 넣는다. 실행 코드의 결과가 예상한 해당 클래스 타입이라면 테스트에 성공한다.
@Test
@DisplayName("assertThrows")
void test4() {
IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> calculator.operate(5, "?", 2));
assertEquals("잘못된 연산자입니다.", exception.getMessage());
}
Given - When - Then 패턴 테스트
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());
}
}
JUnit5 User Guide: https://junit.org/junit5/docs/current/user-guide/#writing-tests-annotations
'Java > Spring' 카테고리의 다른 글
Spring) 비관적 락으로 인한 오류와 해결 (0) | 2023.08.07 |
---|---|
Spring) 동시성 제어 (0) | 2023.08.06 |
Spring) Spring Security (0) | 2023.07.24 |
Spring) refresh token (0) | 2023.07.23 |
23.07.19) CORS 에러 해결 (0) | 2023.07.19 |