6주차 Unit 1.2 — assertThat과 매처(Matcher)

Psj·7일 전

F-lab

목록 보기
183/230

Unit 1.2 — assertThat과 매처(Matcher)

F-LAB JAVA · 6주차 · Phase 1 · JUnit 테스트


📌 학습 목표

이 Unit을 끝내면 다음을 답할 수 있어야 한다.

  • assertThat(actual, matcher) 의 구성은?
  • is() 매처 의 동작은?
  • 전통 assertEquals vs Hamcrest 매처 차이는?
  • 자연어처럼 읽히는 장점은?
  • 자주 쓰는 매처 (nullValue, containsString, greaterThan) 는?
  • 매처 조합 은?
  • JUnit 5 assertEquals vs Hamcrest 선택은?
  • AssertJ 와의 비교는?
  • 단언 실패 메시지 는?

🎯 핵심 한 문장

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 매처.


🧭 9개 섹션 로드맵

1. assertThat 구성
2. is() 매처
3. 전통 assertEquals
4. Hamcrest 비교
5. 자주 쓰는 매처
6. 매처 조합
7. JUnit 5 vs Hamcrest
8. AssertJ 비교
9. 면접 + 자기 점검

1️⃣ assertThat 구성

1.1 기본 형태

import static org.hamcrest.Matchers.is;
import static org.hamcrest.MatcherAssert.assertThat;

assertThat(actual, matcher);
// actual: 검증할 실제 값
// matcher: 만족해야 할 조건

1.2 두 인자

assertThat 두 인자:

1. actual (실제 값)
   - 테스트 대상 결과

2. matcher (매처)
   - 조건
   - 통과 기준

1.3 동작

동작:

  actual 이 matcher 조건 만족:
    - 통과

  만족 X:
    - 실패 (메시지)

1.4 ILIC 의 맥락

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; }
}

1.5 자기 점검 답변

assertThat(actual, matcher) 의 구성은?

:
1. 두 인자:

  • actual (실제)
  • matcher (조건)
  1. actual:

    • 검증 대상 결과
  2. matcher:

    • 통과 기준
  3. 동작:

    • 만족 → 통과

2️⃣ is() 매처

2.1 is()

// is() — equals 비교
assertThat(actual, is(expected));
// actual.equals(expected) 면 통과

2.2 equals 비교

is() = equals 비교:

  is(expected):
    - actual.equals(expected)
    - 같으면 통과

→ 가장 기본 매처

2.3 가독성

// 자연어처럼 읽힘
assertThat(user2.getName(), is(user.getName()));
// "user2 의 이름이 user 의 이름이다"

2.4 데코레이터 역할

is() 데코레이터:

  is(equalTo(x)) = is(x):
    - is 가 equalTo 감쌈
    - 가독성 ↑

  is(matcher) 형태도:
    - is(greaterThan(5))

2.5 ILIC 의 맥락

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) {}

2.6 자기 점검 답변

is() 매처의 동작은?

:
1. is():

  • equals 비교
  1. 동작:

    • actual.equals(expected)
  2. 가독성:

    • 자연어
  3. 데코레이터:

    • is(matcher)

3️⃣ 전통 assertEquals

3.1 assertEquals

// 전통 JUnit
assertEquals(expected, actual);
// expected: 기대 값 (먼저)
// actual: 실제 값 (나중)

3.2 인자 순서

인자 순서 (헷갈림):

  assertEquals(expected, actual):
    - 기대 값 먼저
    - 실제 값 나중

  자주 헷갈림:
    - 순서 바뀌면 메시지 이상

3.3 순서 혼동

// 순서 혼동 문제
assertEquals(user.getName(), user2.getName());
// 어느 게 기대? 어느 게 실제?
// → 실패 메시지 혼란

3.4 단순함

assertEquals 단순함:

  장점:
    - 간단
    - JUnit 기본
    - 추가 라이브러리 X

  단점:
    - 순서 혼동
    - 표현력 ↓

3.5 ILIC 의 맥락

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) {}

3.6 자기 점검 답변

전통 assertEquals는?

:
1. assertEquals:

  • (기대, 실제)
  1. 순서:

    • 기대 먼저
  2. 혼동:

    • 순서 헷갈림
  3. 단순:

    • 간단하나 표현력 ↓

4️⃣ Hamcrest 비교

4.1 비교

// 전통 (JUnit 4)
assertEquals(user.getName(), user2.getName());

// Hamcrest 매처
assertThat(user2.getName(), is(user.getName()));

4.2 가독성 차이

가독성:

assertEquals:
  - "같다 (기대, 실제)"
  - 순서 의존

assertThat + is:
  - "실제가 기대다"
  - 자연어

4.3 표현력

표현력:

assertEquals:
  - 같음만

Hamcrest:
  - is, not, greaterThan
  - containsString
  - 다양한 조건

4.4 실패 메시지

실패 메시지:

Hamcrest:
  Expected: is "BL001"
       but: was "BL002"

→ 명확한 메시지

4.5 ILIC 의 맥락

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; }
}

4.6 자기 점검 답변

전통 assertEquals vs Hamcrest 매처 차이는?

:
1. 가독성:

  • Hamcrest 자연어
  1. 순서:

    • assertEquals 의존
    • Hamcrest 무관
  2. 표현력:

    • Hamcrest 다양
  3. 메시지:

    • Hamcrest 명확

5️⃣ 자주 쓰는 매처

5.1 매처 목록

자주 쓰는 매처:

  is(x): ~이다
  not(x): ~아니다
  nullValue(): 널
  notNullValue(): 널 아님
  containsString(s): 문자열 포함
  greaterThan(x): ~보다 큼
  lessThan(x): ~보다 작음
  hasSize(n): 크기 n
  hasItem(x): 항목 포함

5.2 값 매처

assertThat(value, is(100));               // 같음
assertThat(value, not(0));                // 아님
assertThat(value, greaterThan(50));       // 큼
assertThat(value, lessThanOrEqualTo(200)); // 이하

5.3 널 매처

assertThat(result, nullValue());      // 널
assertThat(result, notNullValue());   // 널 아님

5.4 문자열 매처

assertThat(name, containsString("BL"));      // 포함
assertThat(name, startsWith("BL"));          // 시작
assertThat(name, endsWith("001"));           // 끝

5.5 컬렉션 매처

assertThat(list, hasSize(3));            // 크기
assertThat(list, hasItem("BL001"));      // 항목
assertThat(list, contains("a", "b"));    // 순서대로
assertThat(list, containsInAnyOrder("b", "a"));  // 순서 무관
assertThat(list, empty());               // 빈

5.6 ILIC 의 맥락

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; }
}

5.7 자기 점검 답변

자주 쓰는 매처는?

:
1. :

  • is/not/greaterThan
  1. :

    • nullValue/notNullValue
  2. 문자열:

    • containsString/startsWith
  3. 컬렉션:

    • hasSize/hasItem

6️⃣ 매처 조합

6.1 조합

// 매처 조합
assertThat(value, allOf(greaterThan(0), lessThan(100)));
// 0보다 크고 100보다 작음 (AND)

assertThat(value, anyOf(is(1), is(2), is(3)));
// 1 또는 2 또는 3 (OR)

6.2 allOf / anyOf

조합 매처:

allOf(m1, m2, ...): 모두 만족 (AND)
anyOf(m1, m2, ...): 하나 만족 (OR)
not(m): 부정

6.3 중첩

// 중첩 조합
assertThat(name, allOf(
    startsWith("BL"),
    containsString("001"),
    not(containsString("X"))
));
// BL 로 시작 + 001 포함 + X 미포함

6.4 가독성

조합 가독성:

  복잡한 조건:
    - 여러 매처 조합
    - 한 단언으로
    - 의미 명확

→ 자연어 같은 조건

6.5 ILIC 의 맥락

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; }
}

6.6 자기 점검 답변

매처 조합은?

:
1. 조합:

  • allOf/anyOf
  1. allOf:

    • 모두 (AND)
  2. anyOf:

    • 하나 (OR)
  3. 중첩:

    • 복잡한 조건

7️⃣ JUnit 5 vs Hamcrest

7.1 JUnit 5 assertEquals

// JUnit 5 (org.junit.jupiter)
import static org.junit.jupiter.api.Assertions.*;

assertEquals(expected, actual);
assertTrue(condition);
assertNull(value);
assertThrows(Exception.class, () -> { ... });

7.2 JUnit 5 개선

JUnit 5 개선:

  - assertAll (여러 단언)
  - assertThrows (예외)
  - 람다 메시지 (지연)
  - 충분히 좋음

7.3 선택 기준

선택 기준:

JUnit 5 기본:
  - 단순한 단언
  - 추가 라이브러리 X

Hamcrest:
  - 풍부한 매처
  - 가독성

AssertJ:
  - 플루언트 (체이닝)
  - 현대적

7.4 혼용

// 혼용 가능
@Test
void mixed() {
    // JUnit 5
    assertEquals("BL001", s.getBlNo());
    assertThrows(IllegalArgumentException.class, () -> validate(null));
    
    // Hamcrest
    assertThat(s.getWeight(), greaterThan(BigDecimal.ZERO));
}

7.5 ILIC 의 맥락

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; }
}

7.6 자기 점검 답변

JUnit 5 assertEquals vs Hamcrest 선택은?

:
1. JUnit 5:

  • 단순, 기본
  • assertThrows/assertAll
  1. Hamcrest:

    • 풍부한 매처
  2. 선택:

    • 단순: JUnit 5
    • 풍부: Hamcrest/AssertJ
  3. 혼용:

    • 가능

8️⃣ AssertJ 비교

8.1 AssertJ

// AssertJ (플루언트)
import static org.assertj.core.api.Assertions.assertThat;

assertThat(actual).isEqualTo(expected);
assertThat(name).startsWith("BL").contains("001");
// 체이닝 (메서드 연결)

8.2 플루언트 스타일

플루언트 스타일:

  assertThat(x)
    .isNotNull()
    .isEqualTo(y)
    .hasSize(3);

  → 체이닝, IDE 자동완성

8.3 Hamcrest vs AssertJ

항목HamcrestAssertJ
스타일매처플루언트
자동완성약함강함
가독성자연어체이닝
인기전통현대

8.4 AssertJ 장점

AssertJ 장점:

  - IDE 자동완성 (체이닝)
  - 풍부한 단언
  - 명확한 메시지
  - 컬렉션 강력

→ 현대 선호

8.5 ILIC 의 맥락

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; } }

8.6 자기 점검 답변

AssertJ와의 비교는?

:
1. AssertJ:

  • 플루언트 (체이닝)
  1. 스타일:

    • Hamcrest 매처
    • AssertJ 체이닝
  2. 자동완성:

    • AssertJ 강함
  3. 선호:

    • AssertJ 현대

9️⃣ 면접 + 자기 점검

9.1 면접 단골 질문 매핑

Q핵심 답변
assertThat?(actual, matcher)
is()?equals 비교
assertEquals?(기대, 실제) 순서
Hamcrest 장점?자연어, 표현력
자주 쓰는 매처?is/nullValue/greaterThan
조합?allOf/anyOf
JUnit 5?단순, assertThrows
AssertJ?플루언트
선택?단순/풍부
실패 메시지?Hamcrest 명확

9.2 자기 점검 체크리스트

assertThat

  • 구성

is()

  • equals

assertEquals

  • 순서

Hamcrest

  • 가독성

매처

  • 종류

조합

  • allOf/anyOf

JUnit 5

  • vs Hamcrest

AssertJ

  • 플루언트

9.3 추가 심화 질문

Q1: assertThat 의 static import?

답:

  • org.hamcrest.MatcherAssert.assertThat
  • (JUnit 4 는 org.junit.Assert)
  • 매처: org.hamcrest.Matchers.*
  • import static

Q2: 커스텀 매처?

답:

  • Matcher 구현
  • TypeSafeMatcher 상속
  • describeTo (메시지)
  • 도메인 단언

Q3: 예외 테스트?

답:

  • JUnit 5: assertThrows
  • 반환된 예외 검증
  • 메시지 확인
  • 람다로 실행

Q4: assertAll?

답:

  • 여러 단언 묶기
  • 모두 실행 (중간 실패해도)
  • 전체 결과 보고
  • JUnit 5

Q5: 단언 라이브러리 선택?

답:

  • 팀 컨벤션
  • AssertJ 현대 선호
  • JUnit 5 기본 충분
  • 일관성 중요

🎯 핵심 요약 — 3줄 정리

1. assertThat + 매처

  • assertThat(actual, matcher): 실제 값을 매처와 비교
  • is(expected): equals 비교, 자연어처럼 읽힘

2. 전통 vs Hamcrest

  • assertEquals(기대, 실제): 순서 혼동
  • Hamcrest: is/nullValue/greaterThan/containsString (표현력)

3. 선택

  • JUnit 5 기본 (단순) / Hamcrest (매처) / AssertJ (플루언트)
  • allOf/anyOf 로 조합, 팀 컨벤션 따라

📚 다음으로...

Unit 1.3 — JUnit이 테스트를 실행하는 방식

이번 Unit에서 assertThat/매처를 봤다면, 다음은 JUnit 실행 방식.

  • @Test 메서드 찾기
  • 각 테스트마다 새 인스턴스
  • @BeforeEach/@AfterEach
  • 7단계 실행 흐름

Phase 1 진행 상황

🧪 Phase 1 — JUnit 테스트
  ✅ Unit 1.1 단위 테스트의 필요성
  ✅ Unit 1.2 assertThat과 매처 ← 여기
  ⏭ Unit 1.3 JUnit 실행 방식
  ⏭ Unit 1.4 매번 새 오브젝트
  ⏭ Unit 1.5 픽스처와 @BeforeEach

6주차 누적 진행

🧪 Part A — 학습 도구와 환경
  Phase 1 — JUnit 테스트 (2/5 진행)

총: 2/28 Unit
profile
Software Developer

0개의 댓글