2025. 6. 10. 09:37ㆍspring/TDD
해당 글에서는 JUnit에 대해 이해하고 Spring Boot 환경에서 환경을 구성하는 방법에 대해 알아보고자 한다.
JUnit5
Java언어에서 독립된 단위 테스트(Unit Test)를 지원해주는 프레임워크
JUnit Jupiter의 주요 어노테이션
어노테이션 설명
@Test | 테스트 메서드를 정의하는 데 사용되는 어노테이션입니다. 이 어노테이션이 지정된 메서드는 JUnit Jupiter에서 테스트로 실행됩니다. |
@ParameterizedTest | 매개변수화된 테스트를 정의하는 데 사용되는 어노테이션입니다. 이 어노테이션이 지정된 메서드는 여러 매개변수를 사용하여 반복적으로 실행됩니다. |
@BeforeEach | 각각의 테스트 메서드가 실행되기 전에 실행되는 코드를 정의하는 데 사용되는 어노테이션입니다. |
@AfterEach | 각각의 테스트 메서드가 실행된 후에 실행되는 코드를 정의하는 데 사용되는 어노테이션입니다. |
JUnit5 어노테이션
어노테이션 설명
@Test | 테스트 메소드임을 나타내는 어노테이션 |
@ParameterizedTest | 매개변수화된 테스트를 위한 어노테이션 |
@RepeatedTest | 반복적으로 실행되는 테스트를 위한 어노테이션 |
@TestFactory | 동적으로 테스트를 생성하는 팩토리 메소드를 정의하는 어노테이션 |
@TestTemplate | 테스트 템플릿을 정의하는 어노테이션 |
@TestClassOrder | 테스트 클래스의 실행 순서를 지정하는 어노테이션 |
@TestMethodOrder | 테스트 메소드의 실행 순서를 지정하는 어노테이션 |
@TestInstance | 테스트 인스턴스의 생명주기를 지정하는 어노테이션 |
@DisplayName | 테스트의 표시 이름을 설정하는 어노테이션 |
@DisplayNameGeneration | 테스트의 표시 이름을 동적으로 생성하는 어노테이션 |
@BeforeEach | 각 테스트 메소드 실행 전에 실행되는 메소드를 정의하는 어노테이션 |
@AfterEach | 각 테스트 메소드 실행 후에 실행되는 메소드를 정의하는 어노테이션 |
@BeforeAll | 모든 테스트 메소드 실행 전에 실행되는 메소드를 정의하는 어노테이션 |
@AfterAll | 모든 테스트 메소드 실행 후에 실행되는 메소드를 정의하는 어노테이션 |
@Nested | 중첩된 테스트 클래스를 정의하는 어노테이션 |
@Tag | 테스트를 그룹화하기 위한 태그를 지정하는 어노테이션 |
@Disabled | 비활성화된 테스트를 나타내는 어노테이션 |
@Timeout | 테스트의 제한 시간을 설정하는 어노테이션 |
@ExtendWith | 커스텀 확장을 적용하는 어노테이션 |
@RegisterExtension | 커스텀 확장을 등록하는 어노테이션 |
@TempDir | 테스트용 임시 디렉터리를 생성하는 어노테이션 |
JUnit5 Assertions 메소드
JUnit5 Assertions
- 단위테스트에서는 다양한 유형의 검사를 수행하기 위한 일련의 Assertions메소드를 제공한다.
- 이러한 Assertions는 테스트되는 코드의 예상동작을 확인하는데 도움이 된다.
- import org.junit.jupiter.api.Assertions 패키지를 임포트 하여서 사용
메서드 구분 설명
assertEquals(expected, actual) | JUnit 4~ | 예상값과 실제값이 동일한지 확인합니다. |
assertArrayEquals(expected, actual) | JUnit 4~ | 예상 배열과 실제 배열이 동일한지 확인합니다. |
assertNull(object) | JUnit 4~ | 객체가 null인지 확인합니다. |
assertNotNull(object) | JUnit 4~ | 객체가 null이 아닌지 확인합니다. |
assertSame(expected, actual) | JUnit 4~ | 예상값과 실제값이 같은 객체인지 확인합니다. |
assertNotSame(expected, actual) | JUnit 4~ | 예상값과 실제값이 다른 객체인지 확인합니다. |
assertTrue(condition) | JUnit 4~ | 조건이 참인지 확인합니다. |
assertFalse(condition) | JUnit 4~ | 조건이 거짓인지 확인합니다. |
assertAll((Executable... executables) | JUnit 5 | 모든 단언문(assertion)이 성공하는지 확인합니다. |
assertIterableEquals(Iterable<?> expected, Iterable<?> actual) | JUnit 5 | 예상 가능한 순서로 반복 가능한 항목이 동일한지 확인합니다. |
assertLinesMatch((List<String> expectedLines, List<String> actualLines) | JUnit 5 | 두 문자열 목록이 일치하는지 확인합니다. |
assertNotEquals(Object unexpected, Object actual) | JUnit 5 | 예상값과 실제값이 동일하지 않은지 확인합니다. |
assertThrows(Class<T> expectedType, Executable executable) | JUnit 5 | 지정된 예외가 발생하는지 확인합니다. |
assertTimeout(Duration timeout, Executable executable) | JUnit 5 | 주어진 시간 내에 작업이 완료되는지 확인합니다. |
assertTimeoutPreemptively(Duration timeout, Executable executable) | JUnit 5 | 주어진 시간 내에 작업이 완료되는지 확인하며, 작업이 중단될 수 있습니다 |
JUnit5 활용방안
JUnit 라이프 사이클 수행과정
- 기본적으로 위에서 아래로 @Test 를 선언한 순서에 따라 수행이 된다.
- @BeforeAll JUnit 클래스가 수행되면 최초 한번 @BeforeAll 을 선언한 메소드가 실행
- @BeforeEach @Test를 찾았다면 테스트 실행 전 @BeforeEach 를 선언한 메소드가 실행
- @Test를 선언한 메소드가 실행
- @AfterEach @Test 실행을 마치면 @AfterEach 를 선언한 메소드가 실행
- @Test를 선언한 메서드가 있는지 찾으며 존재하면 (2. 과정)을 반복수행하며,
- 존재하지 않는 경우 (6. 과정)을 수행
- @AfterAll을 선언한 메서드를 수행
- 테스트 종료
AAA(Arrange - Act - Assert) 패턴
- Arrange (준비), Act (실행), Asset(검증)의 약자로, 테스트코드를 구조화
단계 설명
Arrange (준비) | 테스트에 필요한 객체나 데이터를 설정하는 단계로 테스트하려는 객체를 생성하고 모든 의존성을 준비합니다. |
Act (실행) | 준비된 객체나 데이터를 사용하여 실제로 테스트하려는 동작을 실행하는 단계입니다. |
Assert (검증) | 실행 결과가 예상한 대로인지 확인하는 단계. JUnit의 assert 메서드를 사용하여 결과를 검증합니다. |
@SpringBootTest
class MyServiceTest {
@Autowired
private MyService myService;
@Test
void testCalculate() {
// Arrange
int input = 5;
// Act
int result = myService.calculate(input);
// Assert
assertEquals(10, result);
}
}
Given-when-then 패턴
- 테스트 케이스를 더 가독성 있고 유지보수하기 쉽게 구조화하는 방법을 의미
- 이 패턴을 따르면 테스트 케이스가 더 구체적이고 이해하기 쉬워지며
- 각각의 단계를 분리하여 각 테스트 부분이 어떤 역할을 담당하는지 명확
섹션 설명
Given : 설정 | 테스트의 초기 상태 또는 사전 조건을 설정합니다. 입력 데이터나 테스트가 실행될 문맥을 지정합니다. |
When : 동작 | 테스트되는 동작 또는 이벤트를 설명합니다. 테스트되는 특정 메서드나 동작을 나타냅니다. |
Then : 검증 | "When" 섹션에서 설명한 동작으로 인해 기대되는 결과 또는 동작을 정의합니다. |
Given 섹션에서는 '초기 상태'를 설정하고
When 섹션에서는 '특정 동작을 수행'하며,
Then 섹션에서는 '예상 결과를 검증'하는 어서션(assertion)을 사용하여 검증
import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import static org.junit.jupiter.api.Assertions.*;
class MyTest {
@Test
void testListSize() {
// Given
ArrayList<String> list = new ArrayList<>();
// When
list.add("Apple");
list.add("Banana");
list.add("Orange");
// Then
assertEquals(3, list.size());
assertTrue(list.contains("Apple"));
assertFalse(list.isEmpty());
}
}
[ 더 알아보기 ]
💡 AAA 패턴이랑 Given-When-Then 패턴은 결국 똑같은거 아닌가?
- 맞음. 모두 테스트 코드를 구조화하는 데 사용되는 패턴으로, 매우 유사. 각각의 단계는 다음과 같이 대응됨
1. Arrange (준비) = Given (주어진 상황)
- 테스트에 필요한 객체나 데이터를 설정하는 단계
2. Act (실행) = When (실행할 동작)
- 준비된 객체나 데이터를 사용하여 실제로 테스트하려는 동작을 실행
3. Assert (검증) = Then (예상 결과)
- 실행 결과가 예상한 대로인지 확인하는 단계
JUnit 명명 규칙
💡 인기 있는 JUnit 명명 규칙
- JUnit의 메서드명을 지정하는 명명 규칙에 대해 알아보자.
- 이를 통해서 어떤 테스트를 수행하는지 명시적으로 이해할 수 있도록 돕는다.
- 아래의 글을 참고하여서 작성
https://dzone.com/articles/7-popular-unit-test-naming
4.1. 명명 규칙 -1 : '메서드명_테스트대상상태_예상동작' 형식
💡 MethodName_StateUnderTest_ExpectedBehavior
- '메소드명_테스트대상상태_예상동작' 형식으로 JUnit 메서드 명을 지정하는 방식
[ 사용 예시 ]
- isAdult_False_AgeLessThan18: 만약 나이가 18보다 작으면 성인이 아님 (False)을 반환하는 경우
- withdrawMoney_ThrowsException_IfAccountIsInvalid: 잘못된 계정으로 출금 시 예외가 발생하는 경우
- admitStudent_FailToAdmit_IfMandatoryFieldsAreMissing: 필수 입력 필드가 누락되었을 때 학생을 입학시키지 못하는 경우
4.2. 명명 규칙 -2 : '메서드명_예상동작_테스트대상상태 '형식
💡 MethodName_ExpectedBehavior_StateUnderTest
- “메서드명_예상동작_테스트대상상태” 형식으로 JUnit 메서드 명을 지정하는 방식입니다.
[사용 예시]
- isAdult_False_AgeLessThan18: 만약 나이가 18보다 작으면 성인이 아님 (False)을 반환하는 경우
- withdrawMoney_ThrowsException_IfAccountIsInvalid: 잘못된 계정으로 출금 시 예외가 발생하는 경우
- admitStudent_FailToAdmit_IfMandatoryFieldsAreMissing: 필수 입력 필드가 누락되었을 때 학생을 입학시키지 못하는 경우
4.3. 명명 규칙 -3: “test” 접두어 형식
💡 test [Feature being tested]
- “test” 접두어 형식으로 JUnit 메서드 명을 지정하는 방식입니다.
[사용 예시]
- testIsNotAnAdultIfAgeLessThan18: 만약 나이가 18보다 작으면 성인이 아님을 테스트합니다.
- testFailToWithdrawMoneyIfAccountIsInvalid: 잘못된 계정으로 출금 시 실패하는 것을 테스트합니다.
- testStudentIsNotAdmittedIfMandatoryFieldsAreMissing: 필수 입력 필드가 누락되었을 때 학생이 입학되지 않는 것을 테스트합니다.
4.4. 명명 규칙-4 : 테스트할 기능 형식
💡 Feature to be tested
- 일부는 메서드를 테스트 메서드로 식별하기 위해 이미 주석을 사용하고 있으므로, 단순히 테스트할 기능을 작성하는 것이 더 좋다고 제안합니다.
4.5. 명명 규칙 -5: Should_예상동작_테스트대상상태 형식
💡 Should_ExpectedBehavior_When_StateUnderTest
- 'Should_예상동작_When_테스트대상상태'로 JUnit 메서드 명을 지정하는 방식입니다.
[사용예시]
- Should_ThrowException_When_AgeLessThan18: 만약 나이가 18보다 작으면 예외를 던져야 합니다.
- Should_FailToWithdrawMoney_ForInvalidAccount: 잘못된 계정일 경우 출금에 실패해야 합니다.
- Should_FailToAdmit_IfMandatoryFieldsAreMissing: 필수 입력 필드가 누락되었을 때 입학에 실패해야 합니다.
4.6. 명명 규칙 -6: When_테스트대상상태_Expect_예상동작 형식
💡 When_StateUnderTest_Expect_ExpectedBehavior
- 'When_테스트대상상태_Expect_예상동작'으로 JUnit 메서드 명을 지정하는 방식입니다.
[사용예시]
- When_AgeLessThan18_Expect_isAdultAsFalse: 만약 나이가 18보다 작으면 성인이 아님 (False)을 예상합니다.
- When_InvalidAccount_Expect_WithdrawMoneyToFail: 잘못된 계정으로 출금 시 실패할 것을 예상합니다.
- When_MandatoryFieldsAreMissing_Expect_StudentAdmissionToFail: 필수 입력 필드가 누락되었을 때 학생 입학이 실패할 것을 예상합니다.
4.7. 명명 규칙 -7: Given-When-Then 형식
💡 Given_Preconditions_When_StateUnderTest_Then_ExpectedBehavior
- Behavior-Driven Development (BDD)의 일부로 개발된 명명 규칙입니다. 테스트를 사전 조건 (Given), 테스트 대상 상태 (When), 예상 동작 (Then)으로 세 가지 부분으로 나누어 작성합니다.
[사용예시]
- Given_UserIsAuthenticated_When_InvalidAccountNumberIsUsedToWithdrawMoney_Then_TransactionsWillFail: 사용자가 인증된 상태에서 잘못된 계정 번호를 사용하여 금액을 인출하면 거래가 실패할 것입니다.
'spring > TDD' 카테고리의 다른 글
Spring Boot MockMvc 이해하기 : 테스트 흐름 및 사용예제 (1) | 2025.06.12 |
---|---|
Spring Boot Junit5 이해하기 - 환경 구성 및 활용 예제 (1) | 2025.06.10 |
MockMvc, @Transactional(readOnly = true)와 @Transactional을 분리하라고? (0) | 2024.01.23 |
deleteAll()보다 deleteAllInBatch()가 더 권장되는 이유가 뭐야? (0) | 2024.01.15 |
What's BDD? (0) | 2024.01.12 |