F-LAB JAVA · 6주차 · Phase 1 · JUnit 테스트
이 Unit을 끝내면 다음을 답할 수 있어야 한다.
assertThat(actual, matcher) 은 실제 값을 매처와 비교하는 단언으로, is(expected) 같은 Hamcrest 매처를 써서 "실제 값이 기대 값이다" 처럼 자연어에 가깝게 읽히며, 전통적인 assertEquals 보다 표현력과 가독성이 좋다.
assertThat의 첫 인자는 검증할 실제 값 (actual), 둘째 인자는 그 값이 만족해야 할 조건을 나타내는 매처 (matcher) 다.
is(expected)는 equals 비교 매처로,assertThat(user2.getName(), is(user.getName()))은 "user2 의 이름이 user 의 이름이다" 처럼 읽힌다.
전통적인assertEquals(expected, actual)은 인자 순서가 헷갈리기 쉽지만, Hamcrest 는is,nullValue,containsString,greaterThan등 읽기 쉬운 매처 를 제공한다.
최신 JUnit 5 의assertEquals도 충분히 쓸 만하며, 더 풍부한 표현이 필요하면 Hamcrest 나 AssertJ 같은 단언 라이브러리를 선택한다.
assertThat + 매처 = 검사 기준표:
assertThat(실제값, 매처):
- 실제값: 측정한 것
- 매처: 합격 기준
is(기대값):
- "이 값과 같아야"
- assertThat(무게, is(100))
- "무게가 100이다"
전통 (assertEquals):
- assertEquals(100, 무게)
- "100과 무게가 같다?"
- 순서 헷갈림 (기대/실제)
Hamcrest 매처들:
- is(x): ~이다
- nullValue(): 널이다
- greaterThan(x): ~보다 크다
- containsString(s): ~포함
자연어:
assertThat(나이, greaterThan(18))
= "나이가 18보다 크다"
→ 읽으면 의미 명확
→ assertThat(actual, matcher) = 자연어처럼 읽히는 단언, is() 등 Hamcrest 매처.
1. assertThat 구성
2. is() 매처
3. 전통 assertEquals
4. Hamcrest 비교
5. 자주 쓰는 매처
6. 매처 조합
7. JUnit 5 vs Hamcrest
8. AssertJ 비교
9. 면접 + 자기 점검
import static org.hamcrest.Matchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
assertThat(actual, matcher);
// actual: 검증할 실제 값
// matcher: 만족해야 할 조건
assertThat 두 인자:
1. actual (실제 값)
- 테스트 대상 결과
2. matcher (매처)
- 조건
- 통과 기준
동작:
actual 이 matcher 조건 만족:
- 통과
만족 X:
- 실패 (메시지)
import static org.hamcrest.Matchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
class ShipmentDaoTest {
@Test
void add_then_get_returns_same() {
ShipmentDao dao = new ShipmentDao();
Shipment shipment = new Shipment(1L, "BL001", BigDecimal.TEN);
dao.add(shipment);
Shipment found = dao.get(1L);
// assertThat(실제 값, 매처)
assertThat(found.getBlNo(), is("BL001"));
// found.getBlNo() (실제) 가 "BL001" 이다 (매처)
}
}
class ShipmentDao {
void add(Shipment s) {}
Shipment get(Long id) { return null; }
}
record Shipment(Long id, String blNo, java.math.BigDecimal weight) {
String getBlNo() { return blNo; }
}
assertThat(actual, matcher) 의 구성은?
답:
1. 두 인자:
actual:
matcher:
동작:
// is() — equals 비교
assertThat(actual, is(expected));
// actual.equals(expected) 면 통과
is() = equals 비교:
is(expected):
- actual.equals(expected)
- 같으면 통과
→ 가장 기본 매처
// 자연어처럼 읽힘
assertThat(user2.getName(), is(user.getName()));
// "user2 의 이름이 user 의 이름이다"
is() 데코레이터:
is(equalTo(x)) = is(x):
- is 가 equalTo 감쌈
- 가독성 ↑
is(matcher) 형태도:
- is(greaterThan(5))
class FreightTest {
@Test
void freight_calculation() {
FreightCalculator calc = new FreightCalculator();
BigDecimal result = calc.calculate(new Shipment(BigDecimal.valueOf(100)));
// is() — equals 비교
assertThat(result, is(BigDecimal.valueOf(1000)));
// result 가 1000 이다
// 객체 비교
Shipment expected = new Shipment(BigDecimal.valueOf(100));
Shipment actual = new Shipment(BigDecimal.valueOf(100));
assertThat(actual, is(expected)); // equals 비교
}
}
class FreightCalculator {
java.math.BigDecimal calculate(Shipment s) { return null; }
}
record Shipment(java.math.BigDecimal weight) {}
is() 매처의 동작은?
답:
1. is():
동작:
가독성:
데코레이터:
// 전통 JUnit
assertEquals(expected, actual);
// expected: 기대 값 (먼저)
// actual: 실제 값 (나중)
인자 순서 (헷갈림):
assertEquals(expected, actual):
- 기대 값 먼저
- 실제 값 나중
자주 헷갈림:
- 순서 바뀌면 메시지 이상
// 순서 혼동 문제
assertEquals(user.getName(), user2.getName());
// 어느 게 기대? 어느 게 실제?
// → 실패 메시지 혼란
assertEquals 단순함:
장점:
- 간단
- JUnit 기본
- 추가 라이브러리 X
단점:
- 순서 혼동
- 표현력 ↓
import static org.junit.jupiter.api.Assertions.assertEquals;
class TraditionalAssertTest {
@Test
void freight_with_assertEquals() {
FreightCalculator calc = new FreightCalculator();
BigDecimal result = calc.calculate(new Shipment(BigDecimal.valueOf(100)));
// 전통 assertEquals(기대, 실제)
assertEquals(BigDecimal.valueOf(1000), result);
// 순서: 기대 먼저, 실제 나중
// 헷갈리기 쉬움
}
}
class FreightCalculator {
java.math.BigDecimal calculate(Shipment s) { return null; }
}
record Shipment(java.math.BigDecimal weight) {}
전통 assertEquals는?
답:
1. assertEquals:
순서:
혼동:
단순:
// 전통 (JUnit 4)
assertEquals(user.getName(), user2.getName());
// Hamcrest 매처
assertThat(user2.getName(), is(user.getName()));
가독성:
assertEquals:
- "같다 (기대, 실제)"
- 순서 의존
assertThat + is:
- "실제가 기대다"
- 자연어
표현력:
assertEquals:
- 같음만
Hamcrest:
- is, not, greaterThan
- containsString
- 다양한 조건
실패 메시지:
Hamcrest:
Expected: is "BL001"
but: was "BL002"
→ 명확한 메시지
class HamcrestComparison {
@Test
void compare_styles() {
Shipment s = dao.get(1L);
// 전통
// assertEquals("BL001", s.getBlNo());
// Hamcrest (자연어)
assertThat(s.getBlNo(), is("BL001"));
// "blNo 가 BL001 이다"
// 다양한 조건 (Hamcrest 강점)
assertThat(s.getWeight(), is(greaterThan(BigDecimal.ZERO)));
// "weight 가 0보다 크다"
}
}
class ShipmentDao { Shipment get(Long id) { return null; } }
class Shipment {
String getBlNo() { return null; }
java.math.BigDecimal getWeight() { return null; }
}
전통 assertEquals vs Hamcrest 매처 차이는?
답:
1. 가독성:
순서:
표현력:
메시지:
자주 쓰는 매처:
is(x): ~이다
not(x): ~아니다
nullValue(): 널
notNullValue(): 널 아님
containsString(s): 문자열 포함
greaterThan(x): ~보다 큼
lessThan(x): ~보다 작음
hasSize(n): 크기 n
hasItem(x): 항목 포함
assertThat(value, is(100)); // 같음
assertThat(value, not(0)); // 아님
assertThat(value, greaterThan(50)); // 큼
assertThat(value, lessThanOrEqualTo(200)); // 이하
assertThat(result, nullValue()); // 널
assertThat(result, notNullValue()); // 널 아님
assertThat(name, containsString("BL")); // 포함
assertThat(name, startsWith("BL")); // 시작
assertThat(name, endsWith("001")); // 끝
assertThat(list, hasSize(3)); // 크기
assertThat(list, hasItem("BL001")); // 항목
assertThat(list, contains("a", "b")); // 순서대로
assertThat(list, containsInAnyOrder("b", "a")); // 순서 무관
assertThat(list, empty()); // 빈
class MatcherExamples {
@Test
void various_matchers() {
// 값
Shipment s = dao.get(1L);
assertThat(s.getWeight(), is(greaterThan(BigDecimal.ZERO)));
// 널
assertThat(dao.get(999L), is(nullValue()));
// 문자열
assertThat(s.getBlNo(), containsString("BL"));
// 컬렉션
List<Shipment> all = dao.findByStatus("BOOKED");
assertThat(all, hasSize(3));
assertThat(all, hasItem(s));
assertThat(all, is(not(empty())));
}
}
class ShipmentDao {
Shipment get(Long id) { return null; }
java.util.List<Shipment> findByStatus(String s) { return null; }
}
class Shipment {
java.math.BigDecimal getWeight() { return null; }
String getBlNo() { return null; }
}
자주 쓰는 매처는?
답:
1. 값:
널:
문자열:
컬렉션:
// 매처 조합
assertThat(value, allOf(greaterThan(0), lessThan(100)));
// 0보다 크고 100보다 작음 (AND)
assertThat(value, anyOf(is(1), is(2), is(3)));
// 1 또는 2 또는 3 (OR)
조합 매처:
allOf(m1, m2, ...): 모두 만족 (AND)
anyOf(m1, m2, ...): 하나 만족 (OR)
not(m): 부정
// 중첩 조합
assertThat(name, allOf(
startsWith("BL"),
containsString("001"),
not(containsString("X"))
));
// BL 로 시작 + 001 포함 + X 미포함
조합 가독성:
복잡한 조건:
- 여러 매처 조합
- 한 단언으로
- 의미 명확
→ 자연어 같은 조건
class MatcherCombination {
@Test
void combined_matchers() {
Shipment s = dao.get(1L);
// AND 조합
assertThat(s.getWeight(),
allOf(greaterThan(BigDecimal.ZERO),
lessThan(BigDecimal.valueOf(10000))));
// 0 < weight < 10000
// 문자열 복합
assertThat(s.getBlNo(),
allOf(startsWith("BL"),
not(containsString(" "))));
// BL 시작 + 공백 없음
// OR
assertThat(s.getStatus(),
anyOf(is("BOOKED"), is("CONFIRMED"), is("SHIPPED")));
}
}
class ShipmentDao { Shipment get(Long id) { return null; } }
class Shipment {
java.math.BigDecimal getWeight() { return null; }
String getBlNo() { return null; }
String getStatus() { return null; }
}
매처 조합은?
답:
1. 조합:
allOf:
anyOf:
중첩:
// JUnit 5 (org.junit.jupiter)
import static org.junit.jupiter.api.Assertions.*;
assertEquals(expected, actual);
assertTrue(condition);
assertNull(value);
assertThrows(Exception.class, () -> { ... });
JUnit 5 개선:
- assertAll (여러 단언)
- assertThrows (예외)
- 람다 메시지 (지연)
- 충분히 좋음
선택 기준:
JUnit 5 기본:
- 단순한 단언
- 추가 라이브러리 X
Hamcrest:
- 풍부한 매처
- 가독성
AssertJ:
- 플루언트 (체이닝)
- 현대적
// 혼용 가능
@Test
void mixed() {
// JUnit 5
assertEquals("BL001", s.getBlNo());
assertThrows(IllegalArgumentException.class, () -> validate(null));
// Hamcrest
assertThat(s.getWeight(), greaterThan(BigDecimal.ZERO));
}
import static org.junit.jupiter.api.Assertions.*;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
class JUnit5VsHamcrest {
@Test
void shipment_validation() {
Shipment s = dao.get(1L);
// JUnit 5 (단순)
assertNotNull(s);
assertEquals("BL001", s.getBlNo());
// 예외 (JUnit 5)
assertThrows(IllegalArgumentException.class,
() -> dao.get(null));
// Hamcrest (풍부한 매처)
assertThat(s.getWeight(), greaterThan(BigDecimal.ZERO));
assertThat(s.getBlNo(), containsString("BL"));
// 여러 단언 묶기 (JUnit 5)
assertAll(
() -> assertEquals("BL001", s.getBlNo()),
() -> assertNotNull(s.getWeight())
);
}
}
class ShipmentDao { Shipment get(Long id) { return null; } }
class Shipment {
String getBlNo() { return null; }
java.math.BigDecimal getWeight() { return null; }
}
JUnit 5 assertEquals vs Hamcrest 선택은?
답:
1. JUnit 5:
Hamcrest:
선택:
혼용:
// AssertJ (플루언트)
import static org.assertj.core.api.Assertions.assertThat;
assertThat(actual).isEqualTo(expected);
assertThat(name).startsWith("BL").contains("001");
// 체이닝 (메서드 연결)
플루언트 스타일:
assertThat(x)
.isNotNull()
.isEqualTo(y)
.hasSize(3);
→ 체이닝, IDE 자동완성
| 항목 | Hamcrest | AssertJ |
|---|---|---|
| 스타일 | 매처 | 플루언트 |
| 자동완성 | 약함 | 강함 |
| 가독성 | 자연어 | 체이닝 |
| 인기 | 전통 | 현대 |
AssertJ 장점:
- IDE 자동완성 (체이닝)
- 풍부한 단언
- 명확한 메시지
- 컬렉션 강력
→ 현대 선호
import static org.assertj.core.api.Assertions.assertThat;
class AssertJExample {
@Test
void assertj_style() {
Shipment s = dao.get(1L);
// AssertJ 플루언트 (체이닝)
assertThat(s)
.isNotNull()
.extracting(Shipment::getBlNo)
.isEqualTo("BL001");
assertThat(s.getBlNo())
.startsWith("BL")
.contains("001")
.doesNotContain(" ");
// 컬렉션 (강력)
List<Shipment> all = dao.findByStatus("BOOKED");
assertThat(all)
.hasSize(3)
.extracting(Shipment::getBlNo)
.containsExactly("BL001", "BL002", "BL003");
}
}
class ShipmentDao {
Shipment get(Long id) { return null; }
java.util.List<Shipment> findByStatus(String s) { return null; }
}
class Shipment { String getBlNo() { return null; } }
AssertJ와의 비교는?
답:
1. AssertJ:
스타일:
자동완성:
선호:
| Q | 핵심 답변 |
|---|---|
| assertThat? | (actual, matcher) |
| is()? | equals 비교 |
| assertEquals? | (기대, 실제) 순서 |
| Hamcrest 장점? | 자연어, 표현력 |
| 자주 쓰는 매처? | is/nullValue/greaterThan |
| 조합? | allOf/anyOf |
| JUnit 5? | 단순, assertThrows |
| AssertJ? | 플루언트 |
| 선택? | 단순/풍부 |
| 실패 메시지? | Hamcrest 명확 |
답:
답:
답:
답:
답:
1. assertThat + 매처
2. 전통 vs Hamcrest
3. 선택
이번 Unit에서 assertThat/매처를 봤다면, 다음은 JUnit 실행 방식.
🧪 Phase 1 — JUnit 테스트
✅ Unit 1.1 단위 테스트의 필요성
✅ Unit 1.2 assertThat과 매처 ← 여기
⏭ Unit 1.3 JUnit 실행 방식
⏭ Unit 1.4 매번 새 오브젝트
⏭ Unit 1.5 픽스처와 @BeforeEach
🧪 Part A — 학습 도구와 환경
Phase 1 — JUnit 테스트 (2/5 진행)
총: 2/28 Unit