일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
Tags
- 스파르타내일배움캠프
- 문자열
- Firebase
- FSM
- BFS
- 구현
- 이분탐색
- QueryDSL
- 워크플로
- Unity
- unityui
- 순열
- C++
- Unity3d
- 프로그래머스
- Unity2D
- 포톤
- 언리얼엔진
- 유클리드호제법
- c#
- UE4
- 내일배움캠프
- Photon
- Inventory
- 해시
- 스택
- 유니티
- 알고리즘
- UnrealEngine
- 스파르타내일배움캠프TIL
Archives
- Today
- Total
개발 낙서장
단위 테스트 본문
테스트의 필요성
위 그림은 버그를 발견한 시간이 늦어질 수록 비용이 크게 증가하는 것을 의미하는 그래프이다.
Development(개발) 단계에서 발견하면 최소 비용, Unit Tests(단위 테스트) 단계에서 발견하면 중간 비용, QA Testing(QA 테스팅) 단계에서 발견하면 큰 비용, Production(실 서비스 운영 환경) 단계에서 발견하면 매우 큰 비용이 발생한다.
모든 코드는 버그가 발생할 수 있고 그러기에 테스트는 반드시 필요하다.
테스트 코드
테스트 코드란 소프트웨어의 기능과 동작을 테스트하는데 사용되는 코드이다. 어떠한 서비스를 개발했을 때 테스트하기 위해 작성하는 코드이며 반드시 거쳐야 하는 과정이다.
테스트 코드의 종류로는 단위 테스트, 통합 테스트, 시스템 테스트, 사용자 인수 테스트(User Acceptance Testing) 등 다양한 종류가 있으며 테스트 하는 범위, 기댓값 등에 따라 각기 다른 테스트 코드 종류를 선택해 테스트를 진행한다.
단위 테스트
여기서 다뤄볼 것은 단위 테스트인데, 단위 테스트란 말 그대로 코드 단위 별로 테스트를 진행하는 방법이다.
어떤 메소드가 잘 작동하는지를 테스트하며 단위 테스트를 위한 프레임워크인 JUnit5를 많이 사용한다.
JUnit
JUnit 유저 가이드가 존재한다.
- @BeforeEach : 각각의 테스트 코드가 실행되기 전 설정할 게 있을 때 사용
@BeforeEach
void setUp() {
System.out.println("각각의 테스트 코드가 실행되기 전에 수행");
}
- @AfterEach : 각각의 테스트 코드가 실행된 후에 정리할 게 있을 때 사용
@AfterEach
void tearDown() {
System.out.println("각각의 테스트 코드가 실행된 후에 수행");
}
- @BeforeAll : 전체 테스트 전에 설정할 게 있을 때 사용
@BeforeAll
static void beforeAll() {
System.out.println("모든 테스트 코드가 실행되기 전에 최초로 수행");
}
- @AfterAll : 전체 테스트 후에 정리할 게 있을 때 사용
@AfterAll
static void afterAll() {
System.out.println("모든 테스트 코드가 실행된 후에 마지막으로 수행");
}
- @DisplayName : 테스트의 가독성을 위해 사용
@Test
@DisplayName("테스트의 내용을 한눈에 알아볼 수 있게 네이밍")
void test1() {
System.out.println("Test1");
}
- @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 : 파라미터를 받아 테스트하기 위해 사용
@ValueSource를 통해 파라미터 값을 전달할 수 있으며 int, String 등 여러 타입의 파라미터 전달 가능
@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 : 테스트 값이 의도한 값과 같은지 확인
- Assertions.assertNotEquals : 테스트 값이 의도한 값과 다른지 확인
의도한 것과 다른 경우 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);
}
- Assertions.assertTrue(assertFalse) : 해당 값이 True인지 False인지 확인
@Test
@DisplayName("assertTrue 와 assertFalse")
void test2() {
assertTrue(calculator.validateNum(9));
assertFalse(calculator.validateNum(0));
}
- Assertions.assertNotNull(assertNull) : 해당 값이 Null인지 확인
@Test
@DisplayName("assertNotNull 과 assertNull")
void test3() {
Double result1 = calculator.operate(5, "/", 2);
assertNotNull(result1);
Double result2 = calculator.operate(5, "/", 0);
assertNull(result2);
}
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());
}
}
- @BeforeEach를 통해 필요한 객체나 변수(Calculator)를 설정한다. 경우에 따라 @BeforeAll을 사용해 전체 테스트에 공통되는 것들을 설정한다.
- 테스트에 실패했을 경우들을 나누어 테스트 코드를 작성한다.
- 테스트를 진행해보고 예측한 결과와 일치하는지, 다른 놓친 부분은 없는지 체크한다.
'Java' 카테고리의 다른 글
Name 필드 네이밍에 대한 고찰 (0) | 2024.03.05 |
---|---|
[Spring] @DataJpaTest 사용 시 UnsatisfiedDependencyException 발생 (0) | 2024.02.20 |
[Spring] DTO와 Entity (0) | 2024.02.15 |
데이터베이스(DB, DataBase) (0) | 2024.02.13 |
[Spring] Filter 예외 처리 (0) | 2024.02.05 |
Comments