Skip to content

Commit e4f5d56

Browse files
committed
new post valueobejct unittest
1 parent 7541001 commit e4f5d56

File tree

4 files changed

+234
-5
lines changed

4 files changed

+234
-5
lines changed

_data/pageMap.yml

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,16 @@
33
title: 'Spring Data JPA Projections의 생성자 타입 매칭 오류 해결하기'
44
summary: 'JPA Projections 사용 시 발생하는 "Missing constructor" 에러의 원인과 해결 방법을 알아봅니다.'
55
parent: null
6-
url: /blog/2024/03/21/jpa-projects-error
7-
updated: '2024-03-21 15:30:00 +0900'
6+
url: /blog/2025/04/03/jpa-projects-error
7+
updated: '2025-04-03 10:30:00 +0900'
8+
children: []
9+
2025-04-08-test-value-object:
10+
type: blog
11+
title: 'Spring 의존성 없이 도메인 로직 테스트하기 - Money Value Object로 알아보는 효과적인 단위 테스트'
12+
summary: 'Money Value Object 예제를 통해 Spring 의존성 없이 도메인 로직을 효과적으로 테스트하는 방법을 알아봅니다.'
13+
parent: null
14+
url: /blog/2025/04/03/test-value-object
15+
updated: '2025-04-03 10:30:00 +0900'
816
children: []
917
2018년회고:
1018
type: wiki

_data/tagList.yml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,14 @@
1313
- 'bean,'
1414
- book
1515
- 'book,'
16+
- 'CleanCode,'
1617
- codereview
1718
- 'Database,'
1819
- datapipline
1920
- datastructure
2021
- 'db,'
2122
- dbms
23+
- 'DDD,'
2224
- designpattern
2325
- devops
2426
- 'devops,'
@@ -38,8 +40,8 @@
3840
- interface
3941
- jackson
4042
- java
41-
- 'java,'
4243
- 'Java,'
44+
- 'java,'
4345
- 'Java]'
4446
- jpa
4547
- 'JPA,'
@@ -91,11 +93,14 @@
9193
- 'tesla,'
9294
- 'test,'
9395
- 'test,junit'
96+
- 'TestCode]'
9497
- thread
9598
- tip
9699
- transaction
100+
- 'UnitTest,'
97101
- uuid
98102
- validation
103+
- 'ValueObject,'
99104
- webflux
100105
- xss
101106
- y

_data/tagMap.yml

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
- {fileName: 2018년회고}
33
'[SpringBoot,':
44
- {fileName: 2025-04-03-jpa-projects-error}
5+
- {fileName: 2025-04-08-test-value-object}
56
'JPA,':
67
- {fileName: 2025-04-03-jpa-projects-error}
78
'Hibernate,':
@@ -12,6 +13,19 @@
1213
- {fileName: 2025-04-03-jpa-projects-error}
1314
'Java]':
1415
- {fileName: 2025-04-03-jpa-projects-error}
16+
'DDD,':
17+
- {fileName: 2025-04-08-test-value-object}
18+
'ValueObject,':
19+
- {fileName: 2025-04-08-test-value-object}
20+
'UnitTest,':
21+
- {fileName: 2025-04-08-test-value-object}
22+
'Java,':
23+
- {fileName: 2025-04-08-test-value-object}
24+
- {fileName: Optional}
25+
'CleanCode,':
26+
- {fileName: 2025-04-08-test-value-object}
27+
'TestCode]':
28+
- {fileName: 2025-04-08-test-value-object}
1529
'airflow,':
1630
- {fileName: Airflow}
1731
datapipline:
@@ -147,8 +161,6 @@ mysql:
147161
- {fileName: MySQL}
148162
'NoSql,redis':
149163
- {fileName: NoSQL}
150-
'Java,':
151-
- {fileName: Optional}
152164
optional:
153165
- {fileName: Optional}
154166
annoatation:
Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
---
2+
layout : post
3+
title : Spring 의존성 없이 도메인 로직 테스트하기 - Money Value Object로 알아보는 효과적인 단위 테스트
4+
summary : Money Value Object 예제를 통해 Spring 의존성 없이 도메인 로직을 효과적으로 테스트하는 방법을 알아봅니다.
5+
date : 2025-04-03 10:30:00 +0900
6+
tags : [SpringBoot, DDD, ValueObject, UnitTest, Java, CleanCode, TestCode]
7+
toc : true
8+
comment : true
9+
public : true
10+
adsense : true
11+
---
12+
13+
* TOC
14+
{:toc}
15+
16+
# 1. 들어가며
17+
18+
Spring 기반의 애플리케이션을 개발할 때, 테스트 코드 작성은 필수적입니다. 하지만 모든 테스트에 Spring Context가 필요한 것은 아닙니다. 특히 도메인 로직을 테스트할 때는 Spring 의존성 없이도 효과적인 테스트가 가능합니다. 이번 글에서는 Money Value Object를 예제로, Spring 의존성 없이 도메인 로직을 테스트하는 방법과 그 장점을 알아보겠습니다.
19+
20+
# 2. Money Value Object 구현
21+
22+
## 2.1. 기본 구조
23+
24+
Money Value Object는 금액과 통화 정보를 캡슐화한 불변 객체입니다. JPA에서는 `@Embeddable`을 사용하여 엔티티에 내장될 수 있습니다.
25+
26+
```java
27+
@Embeddable
28+
@Getter
29+
@NoArgsConstructor(access = AccessLevel.PROTECTED)
30+
public class Money {
31+
@Column(name = "price", nullable = false)
32+
private BigDecimal price;
33+
34+
@Column(name = "currency", nullable = false)
35+
private String currency;
36+
37+
private Money(BigDecimal price, Currency currency) {
38+
validatePrice(price);
39+
this.price = price;
40+
this.currency = currency.getCurrencyCode();
41+
}
42+
}
43+
```
44+
45+
## 2.2. 팩토리 메서드
46+
47+
```java
48+
public static Money wons(BigDecimal price) {
49+
return new Money(price, Currency.getInstance("KRW"));
50+
}
51+
52+
public static Money dollars(BigDecimal price) {
53+
return new Money(price, Currency.getInstance("USD"));
54+
}
55+
56+
public static Money of(BigDecimal price, Currency currency) {
57+
return new Money(price, currency);
58+
}
59+
```
60+
61+
## 2.3. 비즈니스 로직
62+
63+
```java
64+
public Money add(Money money) {
65+
validateCurrency(money);
66+
return new Money(this.price.add(money.price),
67+
Currency.getInstance(this.currency));
68+
}
69+
70+
public Money multiply(int multiplier) {
71+
return new Money(this.price.multiply(BigDecimal.valueOf(multiplier)),
72+
Currency.getInstance(this.currency));
73+
}
74+
75+
private void validatePrice(BigDecimal price) {
76+
if (price == null) {
77+
throw new IllegalArgumentException("가격은 null일 수 없습니다.");
78+
}
79+
if (price.compareTo(BigDecimal.ZERO) < 0) {
80+
throw new IllegalArgumentException("가격은 0보다 작을 수 없습니다.");
81+
}
82+
}
83+
```
84+
85+
# 3. Spring 의존성 없는 단위 테스트의 장점
86+
87+
## 3.1. 빠른 테스트 실행
88+
- Spring Context 로딩이 필요 없어 테스트 실행 속도가 매우 빠릅니다.
89+
- 개발 중 빈번한 테스트 실행이 가능합니다.
90+
- TDD(테스트 주도 개발) 실천이 용이합니다.
91+
92+
## 3.2. 간단한 테스트 설정
93+
- Spring 관련 설정이나 의존성 주입이 필요 없습니다.
94+
- 순수한 JUnit과 AssertJ만으로 테스트가 가능합니다.
95+
- 테스트 코드 작성의 진입 장벽이 낮아집니다.
96+
97+
## 3.3. 도메인 로직 집중
98+
- 외부 의존성 없이 순수한 비즈니스 로직에만 집중할 수 있습니다.
99+
- 테스트 실패 시 원인 파악이 용이합니다.
100+
- 도메인 로직의 견고성을 높일 수 있습니다.
101+
102+
# 4. Money Value Object 테스트 예제
103+
104+
## 4.1. 기본 생성 테스트
105+
106+
```java
107+
@Test
108+
@DisplayName("원화 Money 객체 생성")
109+
void createKoreanWon() {
110+
// given
111+
BigDecimal amount = new BigDecimal("1000");
112+
113+
// when
114+
Money money = Money.wons(amount);
115+
116+
// then
117+
assertThat(money.getPrice()).isEqualTo(amount);
118+
assertThat(money.getCurrency()).isEqualTo("KRW");
119+
}
120+
```
121+
122+
## 4.2. 연산 테스트
123+
124+
```java
125+
@Test
126+
@DisplayName("같은 통화의 Money 객체 덧셈")
127+
void addMoneyWithSameCurrency() {
128+
// given
129+
Money money1 = Money.wons(new BigDecimal("1000"));
130+
Money money2 = Money.wons(new BigDecimal("2000"));
131+
132+
// when
133+
Money result = money1.add(money2);
134+
135+
// then
136+
assertThat(result.getPrice()).isEqualTo(new BigDecimal("3000"));
137+
assertThat(result.getCurrency()).isEqualTo("KRW");
138+
}
139+
```
140+
141+
## 4.3. 예외 케이스 테스트
142+
143+
```java
144+
@ParameterizedTest
145+
@ValueSource(strings = {"-1000", "-0.01"})
146+
@DisplayName("음수 금액으로 Money 객체 생성 시 예외 발생")
147+
void createMoneyWithNegativeAmount(String amount) {
148+
assertThatThrownBy(() -> Money.wons(new BigDecimal(amount)))
149+
.isInstanceOf(IllegalArgumentException.class)
150+
.hasMessage("가격은 0보다 작을 수 없습니다.");
151+
}
152+
```
153+
154+
# 5. 실제 엔티티에서의 활용
155+
156+
Money Value Object는 실제 엔티티에서도 쉽게 사용할 수 있습니다:
157+
158+
```java
159+
@Entity
160+
@Table(name = "search_products")
161+
public class SearchProduct {
162+
@Embedded
163+
private final Money money;
164+
165+
// 다른 필드들...
166+
}
167+
168+
@Test
169+
@DisplayName("SearchProduct 생성 시 Money 값 객체가 정상적으로 설정되어야 한다")
170+
void createSearchProductWithMoney() {
171+
// given
172+
Money money = Money.wons(new BigDecimal("10000"));
173+
174+
// when
175+
SearchProduct searchProduct = new SearchProduct(
176+
productId, name, imageUrl, money, category, brand, searchRequest
177+
);
178+
179+
// then
180+
assertThat(searchProduct.getMoney())
181+
.isNotNull()
182+
.satisfies(m -> {
183+
assertThat(m.getPrice()).isEqualTo(new BigDecimal("10000"));
184+
assertThat(m.getCurrency()).isEqualTo("KRW");
185+
});
186+
}
187+
```
188+
189+
# 6. 결론
190+
191+
Money Value Object와 같은 도메인 객체를 설계할 때, Spring 의존성을 배제하고 순수한 도메인 로직만으로 구현하면 다음과 같은 이점이 있습니다:
192+
193+
1. **빠른 피드백**: Spring Context 로딩 없이 즉각적인 테스트 실행이 가능합니다.
194+
2. **높은 테스트 커버리지**: 테스트 작성과 실행이 쉬워 더 많은 테스트 케이스 작성이 가능합니다.
195+
3. **도메인 로직 견고성**: 비즈니스 로직에 집중된 테스트로 도메인의 견고성이 향상됩니다.
196+
4. **재사용성**: Spring 의존성이 없어 다른 프로젝트에서도 쉽게 재사용할 수 있습니다.
197+
198+
이러한 접근 방식은 도메인 주도 설계(DDD)의 원칙과도 잘 부합하며, 더 견고하고 유지보수하기 쉬운 코드를 작성하는 데 도움이 됩니다. 특히 비즈니스 로직이 복잡한 엔터프라이즈 애플리케이션에서 이러한 테스트 방식은 더욱 큰 가치를 발휘할 수 있습니다.
199+
200+
# 참고 자료
201+
- [Domain-Driven Design: Tackling Complexity in the Heart of Software](https://www.amazon.com/Domain-Driven-Design-Tackling-Complexity-Software/dp/0321125215)
202+
- [Effective Unit Testing](https://www.manning.com/books/effective-unit-testing)
203+
- [Spring Framework Documentation](https://docs.spring.io/spring-framework/docs/current/reference/html/)
204+

0 commit comments

Comments
 (0)