-
Better Assertions with AssertJ by Tim te Beek @ Spring I/O 2025Spring/Spring IO 2025. 9. 20. 20:10
Better Assertions with AssertJ by Tim te Beek @ Spring I/O 2025
https://www.youtube.com/watch?v=k7sXn1v4fYc&
https://2025.springio.net/slides/better-assertions-with-assertj-springio25.pdf
2025년 5월 22-23일에 개최된 Spring I/O 컨퍼런스에서 Moderne의 Staff Software Engineer인 Tim te Beek이 발표한 'AssertJ를 사용한 더 나은 단언(Assertions)'에 대한 발표 자료와 강연 내용을 분석
1단계: 발표 개요 및 핵심 주제
구분 내용 발표자 Tim te Beek (Moderne/OpenRewrite) 행사 Spring I/O 2025 주요 목표 AssertJ의 Fluent Assertions API를 소개하고, JUnit과 Hamcrest 같은 기존 라이브러리와 비교하여 테스트 실패 시 더 표현력이 풍부하고 의미 있는 메시지를 제공하는 방법을 설명합니다. 핵심 메시지 코드 품질 개선을 위해 Assertion 라이브러리를 AssertJ로 즉시, 일관성 있게 통일하고, OpenRewrite를 통해 대규모 마이그레이션을 자동화해야 합니다 [01:38, 34:53].
2단계: 기존 Assertion 방식의 문제점 (Bad Assertions)
Tim te Beek은 기존 테스트 방식에서 발견되는 여러 가지 문제점(Anti-patterns)을 지적했습니다 [05:03].
- 테스트가 전혀 없는 경우: 가장 심각한 문제로, 테스트 커버리지를 확보하기 위해 Diff Blue Cover와 같은 도구를 사용하여 회귀 테스트를 생성하는 것을 제안했습니다 [05:03, 05:27].
- 불충분한 단언: 단순히 코드가 실행되는지만 확인하고 실제 결과에 대한 단언을 생략하는 방식 [06:15].
- 표현력이 부족한 메시지: JUnit의 assertEquals()와 같은 기본 단언은 실패 시 "Expected 'true', was 'false'"와 같이 문제의 원인을 파악하기 어려운 모호한 오류 메시지를 반환합니다 [10:05, 11:00]. 이로 인해 디버깅 시간이 길어집니다.
- Hamcrest의 복잡성: Hamcrest는 표현력은 높지만, 다양한 정적 가져오기(Static Imports)를 사용해야 하므로 API가 발견하기 어렵고(Less Discoverable), hasItem과 contains처럼 이름이 유사한 메서드 간의 미묘한 차이로 인해 오용되기 쉽습니다 [18:18, 20:09].
- 잘못된 마이그레이션: JUnit 4에서 5로 수동 마이그레이션 시 assertTrue(message != null)과 같이 유효성 검사에 도움이 되지 않는 코드가 삽입되는 등 오류가 발생할 수 있습니다 [11:46, 12:42].
Hamcrest란?
✅ Hamcrest란? (AssertJ에서의 관점)
Hamcrest는 매처(matcher) 기반의 단언 프레임워크로, JUnit과 함께 자주 사용됩니다. AssertJ에서 Hamcrest를 언급할 때는 보통 다음과 같은 맥락에서 비교됩니다:
- 표현 방식: Hamcrest는 assertThat(value, matcher) 형식으로 작성합니다.
-
java
assertThat(name, is("HyungSeob")); assertThat(score, greaterThan(80)); - 장점:
- 다양한 매처 제공 (is, containsString, hasItem, 등)
- 매처 조합 가능 (allOf, anyOf, 등)
- 단점:
- 문법이 덜 직관적이고, 오류 메시지가 덜 상세함
- 커스터마이징이 제한적
🌟 AssertJ의 시각에서 Hamcrest와의 차이점
AssertJ는 Hamcrest보다 더 유연하고 읽기 쉬운 API를 제공합니다. 예를 들어:
javaassertThat(name).isEqualTo("HyungSeob"); assertThat(score).isGreaterThan(80);AssertJ의 특징:
- Fluent API: 체이닝 방식으로 여러 단언을 연결 가능
- 더 풍부한 단언 메서드: 객체, 리스트, 맵 등 다양한 타입 지원
- 커스터마이징 가능: 사용자 정의 단언 및 오류 메시지 설정 가능
- Hamcrest와 호환 가능: 필요 시 Hamcrest 매처도 함께 사용할 수 있음
🔍 요약 비교
항목HamcrestAssertJ문법 스타일 assertThat(value, matcher) assertThat(value).isEqualTo(...) 오류 메시지 비교적 간단함 상세하고 커스터마이징 가능 확장성 제한적 매우 유연함 학습 곡선 다소 높음 직관적이고 쉬움 커스터마이징 제한적 가능
3단계: AssertJ의 장점 및 주요 기능 (Better Assertions)
AssertJ는 기존 라이브러리의 단점을 개선한 연속적인(Chained) 단언 스타일을 제공하여 테스트를 더 읽기 쉽고 강력하게 만듭니다 [22:34].
AssertJ 주요 특징 설명 플루언트 API
(Fluent API)assertThat(actual).isNotNull().contains(expected).hasSize(3)와 같이 메서드를 연결하여 자연스러운 문장처럼 Assertion 을 작성할 수 있습니다 [22:47]. 명확한 오류 메시지 실패 시 어떤 객체의 어떤 속성에서 문제가 발생했는지에 대한 구체적인 컨텍스트를 포함한 오류 메시지를 제공하여 디버깅이 쉬워집니다 [28:44, 29:03]. 커스텀 단언
(Custom Assertions)AbstractObjectAssert를 확장하여 복잡한 도메인 객체(예: BookTester)에 대한 맞춤형 Assertion 메서드를 정의할 수 있어 코드의 표현력이 극대화됩니다 [27:31, 30:07]. Soft Assertions/Certify 여러 개의 단언 중 하나만 실패해도 테스트가 즉시 중단되는 대신, 모든 단언을 실행한 후 실패한 모든 항목에 대한 요약을 보고합니다. 이는 복잡한 DTO 검증에 유용합니다 [32:46, 33:54]. Sheets로 내보내기Junit4 vs Junit5

Junit 5 의 assertThrows 가 테스트 의도를 명확히 할수 있음
@Test public void testLocalSchedulerEnabled() { assertThrows(NoSuchBeanDefinitionException.class, () -> { assertFalse(context.getEnvironment().containsProperty("kubernetes_service_host")); assertFalse(CloudPlatform.CLOUD_FOUNDRY.isActive(context.getEnvironment())); context.getBean(Scheduler.class); }); }
4단계: Spring 통합 및 대규모 전환 자동화
1. MockMvcTester를 통한 Assertion 통일 (Unified Assertions)
- **Spring Framework 6.2 (Spring Boot 3.4)**부터 도입된 MockMvcTester는 AssertJ를 사용하여 Spring WebMVC 테스트의 결과를 단언할 수 있게 합니다 [00:39:24, 00:40:12, 547].
- 기존에 MockMvc 테스트에서 Hamcrest를 강제로 사용해야 했던 불편함을 해소하고, 이제 모든 테스트에 AssertJ 하나만 사용할 수 있게 되었습니다 [33:41, 41:20].
- 예시: assertThat(mockMvc.get("/")).isStatus().isOk() [39:38].
2. OpenRewrite를 사용한 자동 마이그레이션 및 적용
- 발표자는 자신이 일하는 Moderne의 OpenRewrite 도구를 사용하여 AssertJ로의 대규모 전환을 자동화하는 방법을 강조했습니다 [00:34:53, 00:35:18, 523].
- 자동 수정 예시:
- JUnit 4의 @Test(expected=...)를 JUnit 5의 **assertThrows(() -> {})**로 코드 스타일을 유지하며 정확하게 변환.
- Hamcrest 또는 기존 JUnit의 단언을 AssertJ의 Fluent 스타일로 변환하는 Refaster 규칙 적용. (예: assertThat(string.contains(substring)).isTrue() assertThat(string).contains(substring)) [45:15].
- GitHub Pull Request에 봇으로 자동 코드 리뷰 및 개선 제안을 하여 팀원들의 새로운 패턴 습득을 돕고 올바른 사용을 장려합니다 [00:43:09, 00:44:15, 554, 559].

'Spring > Spring IO' 카테고리의 다른 글