TDD(테스트 주도 개발, Test-Driven Development) & Junit
TDD(Test-Driven Development)
TDD는 "테스트를 먼저 작성하고 코드를 구현하는 방식"으로, 소프트웨어 개발을 더욱 견고하고 신뢰성 있게 만드는 방법론입니다. TDD의 핵심 과정은 반복적으로 테스트 작성 -> 코드 구현 -> 리팩토링을 통해 코드를 점진적으로 개선해 나가는 것입니다.
TDD 개발 프로세스
Red: 실패하는 테스트 작성
- 먼저 실패하는 테스트 코드를 작성합니다. 이 단계에서 JUnit을 활용해, 단위 테스트 메서드를 작성하고 이 테스트가 실행되었을 때 실패하도록 합니다.
- 실패를 통해 "구현되지 않은 기능"을 명확히 인지하고, 필요한 기능에 대한 요구사항을 확인하게 됩니다.
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
public class CalculatorTest {
@Test
void testAdd() {
Calculator calculator = new Calculator();
// 이 코드는 실패: add 메서드가 아직 없거나 메서드 내부에 기능이 구현되지 않음
assertEquals(5, calculator.add(2, 3));
}
}
Green: 테스트를 통과하는 최소한의 코드 작성
- 테스트가 성공하도록 하는 최소한의 코드를 작성합니다. 이때는 가독성이나 최적화보다는 테스트를 통과시키는 것에 집중합니다.
- 이 단계가 끝나면 JUnit 테스트를 실행했을 때 모든 테스트가 통과하는 초록색 성공 표시를 확인할 수 있습니다.
public class Calculator {
public int add(int a, int b) {
return a + b;
}
}
Refactor: 코드 개선 및 리팩토링
- 테스트가 통과되면, 이제 코드의 가독성과 성능을 개선합니다.
- JUnit 테스트를 다시 실행해 리팩토링 후에도 기능이 정상 작동하는지 확인합니다.
Junit, 단위 테스트
JUnit는 Java에서 가장 널리 사용되는 단위 테스트 프레임워크 중 하나로, 코드의 특정 부분이 예상대로 작동하는지 검증하는 데 사용됩니다.
JUnit은 주로 테스트 메서드를 작성하고 실행하는 기능을 제공하여 개발자가 개별 메서드나 클래스의 동작을 독립적으로 테스트할 수 있도록 해줍니다. 특히 JUnit은 단위 테스트뿐만 아니라 통합 테스트에서도 널리 사용됩니다.
Given, When, Then 패턴
- JUnit에서 Given-When-Then 패턴은 단위 테스트를 작성할 때 코드를 더 명확하고 읽기 쉽게 작성하도록 도와주는 구조입니다. 테스트 시나리오를 세 단계로 나누어 기대하는 결과를 예측하고 확인할 수 있게 합니다.
1. Given (준비)
- 테스트에 필요한 상황을 설정하는 단계입니다.
- 테스트 대상 객체를 생성하고, 필요한 초기 상태나 조건을 설정합니다.
// Given
Calculator calculator = new Calculator();
int a = 5;
int b = 3;
2. When (실행)
- 테스트할 동작을 수행하는 단계입니다.
- 보통 하나의 메서드를 호출하거나 특정한 행위를 수행하는 부분이 여기에 들어갑니다.
// When
int result = calculator.add(a, b);
3. Then (검증)
- 테스트 결과가 예상과 일치하는지 검증하는 단계입니다.
assertEquals
,assertTrue
등과 같은 JUnit의 검증 메서드를 사용하여 실제 결과가 예상 결과와 같은지 확인합니다.
// Then
assertEquals(8, result);
전체 코드
@Test
void testAdd() {
// Given: 초기 상태를 설정
Calculator calculator = new Calculator();
int a = 5;
int b = 3;
// When: 특정 동작 수행
int result = calculator.add(a, b);
// Then: 결과 확인
assertEquals(8, result);
}
Junit 애노테이션
- 단위테스트를 수행하기 위해 사용하는
@Test
애노테이션 외에도 유용한 애노테이션들이 있습니다. 테스트 과정에서 중복 코드를 줄이고 가독성을 향상 시키는데 도움을 줍니다.
1. @BeforeEach: 각각의 테스트 메서드가 수행되기전에 수행
- 테스트 메서드에서 사용되는 객체를 초기화 하는데 유용합니다.
아래 코드에서는 각 테스트 메서드가 실행되기 전에 setup()
메서드가 호출됩니다. 두 개의 테스트가 각각 실행될 때마다 Calculator
인스턴스가
초기화됩니다.
@BeforeEach
void setup() {
// 테스트마다 Calculator 인스턴스 생성
calculator = new Calculator();
}
@Test
void testAdd() {
int result = calculator.add(2, 3);
assertEquals(5, result);
}
@Test
void testSubtract() {
int result = calculator.subtract(5, 3);
assertEquals(2, result);
}
2. @BeforeAll: 모든 테스트가 실행되기 전, 한 번만 수행
- 일반적으로 무거운 리소스를 초기화하거나, 테스트 클래스 내에서 한 번만 생성되어야 하는 공유 자원이 있을 때 사용됩니다.
@BeforeAll
메서드는static
으로 선언해야 합니다.
@BeforeAll
static void globalSetup() {
// 테스트 DB 연결 설정
databaseConnection = new DatabaseConnection();
databaseConnection.connect();
}
3. @AfterEach: 각 테스트 메서드가 수행된 후에 수행
- 주로 테스트 중 변경된 리소스를 정리하거나, 데이터베이스에서 데이터 삭제 등 상태를 원래대로 돌릴 때 사용됩니다.
@AfterEach
void tearDown() {
// Calculator 인스턴스를 null로 설정해 해제
calculator = null;
}
4. @AfterAll: 모든 테스트가 완료되고 한 번만 수행
- 주로
@BeforeAll
에서 설정한 리소스를 정리하거나, 전체 테스트 종료 후 수행해야 할 정리 작업을 넣습니다. @AfterAll
메서드도static
으로 선언해야 합니다.
@AfterAll
static void globalTearDown() {
// 테스트 DB 연결 해제
databaseConnection.disconnect();
}
Assertions, 테스트 검증
- Assertions는 테스트의 예상 결과와 실제 결과를 비교해 성공, 실패를 판단합니다.
- JUnit에서 제공하는
assertEquals, assertTrue, assertFalse, assertNotNull
같은 메서드를 사용하여 테스트 결과를 검증할 수 있습니다.
1. assertEquals(expected, actual)
- 예상 값과 실제 값이 같은지를 확인합니다.
@Test
void testAddition() {
int result = Calculator.add(2, 3); // add 메서드는 2 + 3을 계산한다고 가정
assertEquals(5, result, "2 + 3은 5가 되어야 합니다.");
}
2. assertTrue(condition), assertFalse(condition)
- 조건이 참(true) 혹은 거짓(false) 인지 확인합니다.
@Test
void testIsPositive() {
int number = 10;
assertTrue(number > 0, "number는 양수여야 합니다.");
}
@Test
void testIsNegative() {
int number = -5;
assertFalse(number >= 0, "number는 음수여야 합니다.");
}
3. assertNotNull(object)
- 객체가 null이 아님을 확인합니다.
@Test
void testFindUser() {
User user = userService.findUserById(1); // ID가 1인 사용자 검색
assertNotNull(user, "ID가 1인 사용자는 존재해야 합니다.");
}
4. assertThrows
- 특정 예외가 발생하는지를 확인합니다. 예상된 예외가 발생할 경우에만 성공하도록 설계되어 있습니다.
- 코드가 예상대로 잘못된 상황에서 예외 처리 로직을 테스트하는 데 유용합니다.
@Test
void testExceptionThrowing() {
// 예외를 던질 것으로 예상하는 코드 블록을 assertThrows로 감싸기
assertThrows(IllegalArgumentException.class, () -> {
// 테스트할 코드 (예외를 던질 코드)
myService.someMethod(null);
});
}
- assertThrows 는 던져진 예외 객체를 반환하므로, 이 예외를 이용해 예외 메시지나 기타 정보를 추가로 검증할 수 있습니다.
@Test
void testExceptionWithMessage() {
Exception exception = assertThrows(IllegalArgumentException.class, () -> {
myService.someMethod(null);
});
// 예외 메시지 확인
assertEquals("Invalid input", exception.getMessage());
}
반복 테스트, @ParameterizedTest & @ValueSource
- @ParameterizedTest, @ValueSource 는 Junit에서 반복 테스트를 할수 있도록 하는 애노테이션 입니다.
- 다양한 입력값으로 반복해 검증할 때 사용되며, 테스트 코드의 효율성과 가독성을 높이는 데 유용합니다.
@ParameterizedTest
@Test
대신 사용되며, 여러 입력값을 받아 같은 테스트를 반복 실행합니다.
@ValueSource
- @ParameterizedTest와 함께 사용되며, 다양한 단일 매개변수를 간편하게 제공하는 역할을 합니다.
- 기본적으로 int, double, String, long, short, char, byte, boolean 배열을 인수로 받을 수 있습니다.
예제 코드
@ParameterizedTest
@ValueSource(strings = {"apple", "banana", "cherry"})
void testWithMultipleValues(String fruit) {ㄴ
assertTrue(fruit.length() > 0);
}
@ParameterizedTest
@ValueSource(ints = {1, 2, 3, 4, 5})
void testWithIntValues(int number) {
assertTrue(number > 0);
}
반응형
'WEB > Spring' 카테고리의 다른 글
의존성 주입 애노테이션 @RequiredArgsConstructor, @AllArgsConstructor, @NoArgsConstructor 핵심 정리 (0) | 2024.10.31 |
---|---|
[Spring] 의존관계 주입(Dependency Injection) 개념과 Bean 중복방지 (0) | 2023.12.14 |
[Spring] @ComponentScan, Bean 객체 관리하기 (0) | 2023.12.14 |
[Spring] @Configuration과 싱글톤 패턴의 관계 (0) | 2023.12.13 |
[Spring] 싱글톤 패턴과 싱글톤 컨테이너 개념 (1) | 2023.12.13 |
댓글