2025-04-08T19:56:24
This commit is contained in:
201
docs/test/04_테스트 유틸리티와 도구.md
Normal file
201
docs/test/04_테스트 유틸리티와 도구.md
Normal file
@@ -0,0 +1,201 @@
|
||||
아래는 **4장. 테스트 유틸리티와 도구**에 대한 글입니다. 이 장은 스프링 부트 테스트를 더 효율적이고 가독성 있게 작성할 수 있도록 돕는 유틸리티와 도구를 소개하며, 실습 예제를 포함해 실무에서의 활용성을 강조했습니다.
|
||||
|
||||
---
|
||||
|
||||
## 4장. 테스트 유틸리티와 도구
|
||||
|
||||
테스트 코드는 단순히 동작을 검증하는 것을 넘어, 가독성 있고 유지보수하기 쉬워야 합니다. 스프링 부트에서는 이를 지원하는 다양한 유틸리티와 도구가 제공됩니다. 이 장에서는 AssertJ와 Hamcrest로 검증을 강화하고, 스프링 REST Docs로 API 문서를 생성하며, 테스트 코드 리팩토링 팁을 다룹니다.
|
||||
|
||||
### 4.1 AssertJ로 깔끔한 검증 작성
|
||||
|
||||
AssertJ는 JUnit의 기본 `assert` 메서드보다 더 유연하고 가독성 높은 검증을 제공하는 라이브러리입니다. `spring-boot-starter-test`에 기본 포함되어 있어 별도 설정 없이 바로 사용할 수 있습니다. AssertJ의 장점은 메서드 체이닝으로 자연스러운 문장을 만들고, 풍부한 검증 옵션을 제공한다는 점입니다.
|
||||
|
||||
예를 들어, 기본 JUnit 검증과 AssertJ를 비교해 봅시다:
|
||||
|
||||
```java
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class AssertJExampleTest {
|
||||
|
||||
@Test
|
||||
void basicJUnitAssertion() {
|
||||
String result = "Hello, World!";
|
||||
assertEquals("Hello, World!", result); // JUnit 기본 방식
|
||||
}
|
||||
|
||||
@Test
|
||||
void assertJAssertion() {
|
||||
String result = "Hello, World!";
|
||||
assertThat(result)
|
||||
.isNotNull()
|
||||
.startsWith("Hello")
|
||||
.contains("World")
|
||||
.hasLength(13); // AssertJ 방식
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
AssertJ는 리스트, 맵, 객체 등 복잡한 데이터 구조도 쉽게 검증할 수 있습니다:
|
||||
|
||||
```java
|
||||
@Test
|
||||
void assertJListTest() {
|
||||
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
|
||||
assertThat(names)
|
||||
.hasSize(3)
|
||||
.containsExactly("Alice", "Bob", "Charlie")
|
||||
.doesNotContain("David");
|
||||
}
|
||||
```
|
||||
|
||||
AssertJ를 사용하면 실패 메시지도 더 자세히 제공되어 디버깅이 쉬워집니다.
|
||||
|
||||
### 4.2 Hamcrest를 활용한 유연한 매칭
|
||||
|
||||
Hamcrest는 테스트에서 조건을 더 유연하고 읽기 쉽게 표현할 수 있는 매처(matcher)를 제공합니다. AssertJ와 비슷한 역할을 하지만, 문법적으로 다르며 특정 상황에서 더 직관적일 수 있습니다. `spring-boot-starter-test`에도 포함되어 있습니다.
|
||||
|
||||
Hamcrest의 기본 사용법은 다음과 같습니다:
|
||||
|
||||
```java
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
|
||||
class HamcrestExampleTest {
|
||||
|
||||
@Test
|
||||
void hamcrestBasicTest() {
|
||||
String result = "Hello, World!";
|
||||
assertThat(result, is("Hello, World!")); // 기본 매칭
|
||||
assertThat(result, startsWith("Hello"));
|
||||
assertThat(result, containsString("World"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void hamcrestCollectionTest() {
|
||||
List<Integer> numbers = Arrays.asList(1, 2, 3, 4);
|
||||
assertThat(numbers, hasSize(4));
|
||||
assertThat(numbers, hasItem(greaterThan(2)));
|
||||
assertThat(numbers, everyItem(lessThan(5)));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Hamcrest는 특히 컬렉션이나 복잡한 조건을 테스트할 때 유용하며, AssertJ와 함께 사용해도 무방합니다. 상황에 따라 두 도구를 혼합해 가독성을 높일 수 있습니다.
|
||||
|
||||
### 4.3 스프링 REST Docs로 API 문서화
|
||||
|
||||
스프링 REST Docs는 테스트를 기반으로 REST API 문서를 자동 생성하는 도구입니다. 기존 Swagger와 달리 테스트가 실패하면 문서도 갱신되지 않으므로, 항상 정확한 문서를 유지할 수 있습니다. 설정은 약간 복잡하지만, 품질 높은 문서를 위해 투자할 가치가 있습니다.
|
||||
|
||||
의존성을 추가합니다:
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>org.springframework.restdocs</groupId>
|
||||
<artifactId>spring-restdocs-mockmvc</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
테스트 예제:
|
||||
|
||||
```java
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
|
||||
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
|
||||
import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
@WebMvcTest(UserController.class)
|
||||
@AutoConfigureRestDocs(outputDir = "target/snippets")
|
||||
class UserControllerRestDocsTest {
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@Test
|
||||
void getUserName_documentation() throws Exception {
|
||||
mockMvc.perform(get("/users/1/name"))
|
||||
.andExpect(status().isOk())
|
||||
.andDo(document("user-get-name",
|
||||
responseFields(
|
||||
fieldWithPath("name").description("The name of the user")
|
||||
)));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
위 코드는 `target/snippets` 폴더에 문서 조각을 생성하며, 이를 Asciidoctor로 통합해 HTML 문서로 변환할 수 있습니다. 예를 들어, `pom.xml`에 빌드 플러그인을 추가해 문서를 생성합니다:
|
||||
|
||||
```xml
|
||||
<plugin>
|
||||
<groupId>org.asciidoctor</groupId>
|
||||
<artifactId>asciidoctor-maven-plugin</artifactId>
|
||||
<version>2.2.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>generate-docs</id>
|
||||
<phase>prepare-package</phase>
|
||||
<goals>
|
||||
<goal>process-asciidoc</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<sourceDirectory>src/docs/asciidoc</sourceDirectory>
|
||||
<attributes>
|
||||
<snippets>${project.build.directory}/snippets</snippets>
|
||||
</attributes>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
```
|
||||
|
||||
### 4.4 테스트 코드 리팩토링 팁
|
||||
|
||||
좋은 테스트 코드는 명확하고 중복이 적어야 합니다. 다음은 리팩토링 팁입니다:
|
||||
|
||||
- **공통 설정 분리**: `@BeforeEach`나 별도의 유틸리티 클래스를 활용해 반복 코드를 줄입니다.
|
||||
```java
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
userRepository.save(new User(1L, "Alice"));
|
||||
}
|
||||
```
|
||||
- **테스트 메서드 이름 명확히**: `getUserName_withExistingUser_returnsName`처럼 의도를 드러내게 작성합니다.
|
||||
- **작은 테스트 유지**: 한 테스트가 너무 많은 것을 검증하지 않도록 단일 책임 원칙을 따릅니다.
|
||||
- **커스텀 Matcher/Assertion 사용**: AssertJ나 Hamcrest로 자주 사용하는 검증 로직을 재사용 가능하게 만듭니다.
|
||||
|
||||
예를 들어, 커스텀 AssertJ assertion:
|
||||
```java
|
||||
public class UserAssertions {
|
||||
public static AbstractStringAssert<?> assertThatUserName(String actual) {
|
||||
return assertThat(actual).isNotNull().isNotEmpty();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
사용:
|
||||
```java
|
||||
@Test
|
||||
void testUserName() {
|
||||
String name = userService.getUserName(1L);
|
||||
UserAssertions.assertThatUserName(name).isEqualTo("Alice");
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 마무리
|
||||
|
||||
AssertJ와 Hamcrest는 테스트 검증을 풍부하게 하고, 스프링 REST Docs는 API 문서화를 혁신하며, 리팩토링은 테스트 코드의 품질을 높입니다. 이러한 도구와 기법을 활용하면 테스트가 단순한 검증을 넘어 팀과 프로젝트에 실질적인 가치를 더할 수 있습니다. 다음 장에서는 테스트 데이터 관리 전략을 다루며, 통합 테스트를 더 깊이 탐구해 보겠습니다!
|
||||
|
||||
---
|
||||
|
||||
이 글은 도구별 특징과 실습 예제를 중심으로 작성되었으며, 실무에서의 활용성을 높였습니다. 추가 요청이 있다면 말씀해 주세요!
|
||||
Reference in New Issue
Block a user