AssertJ란?
AssertJ는 Java 테스트에서 유창하고(fluent) 풍부한 단언(assertion)을 작성할 수 있게 해주는 오픈소스 라이브러리입니다. 기존의 JUnit assert보다 훨씬 읽기 쉽고 직관적인 테스트 코드를 작성할 수 있습니다.
기존 방식 vs AssertJ
// 기존 JUnit 방식
assertEquals("Frodo", frodo.getName());
assertTrue(fellowshipOfTheRing.contains(frodo));
assertEquals(9, fellowshipOfTheRing.size());
// AssertJ 방식
assertThat(frodo.getName()).isEqualTo("Frodo");
assertThat(fellowshipOfTheRing)
.hasSize(9)
.contains(frodo, sam)
.doesNotContain(sauron);
보시다시피 AssertJ는 마치 자연어처럼 읽히는 테스트 코드를 작성할 수 있게 해줍니다.
설정 방법
Maven 의존성 추가
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>3.26.0</version>
<scope>test</scope>
</dependency>
정적 임포트
import static org.assertj.core.api.Assertions.*;
이 한 줄의 임포트만으로 AssertJ의 모든 기능을 사용할 수 있습니다.
핵심 사용법
1. 기본 구조
AssertJ의 모든 단언은 assertThat() 메서드로 시작합니다:
assertThat(실제값).기대하는조건();
주의: assertThat(값)만 작성하고 조건을 추가하지 않으면 실제로는 아무것도 검증하지 않습니다!
2. 객체 검증
public class Dog {
private String name;
private Float weight;
// getters, setters...
}
Dog fido = new Dog("Fido", 5.25f);
Dog fidosClone = new Dog("Fido", 5.25f);
// 참조 비교 (실패)
assertThat(fido).isEqualTo(fidosClone);
// 필드별 재귀 비교 (성공)
assertThat(fido).isEqualToComparingFieldByFieldRecursively(fidosClone);
3. 불린 검증
assertThat("".isEmpty()).isTrue();
assertThat("hello".isEmpty()).isFalse();
4. 컬렉션/배열 검증
List<String> list = Arrays.asList("1", "2", "3");
assertThat(list)
.isNotEmpty()
.hasSize(3)
.contains("1")
.doesNotContain("4")
.startsWith("1")
.doesNotContainNull()
.containsSequence("2", "3");
5. 문자열 검증
assertThat(frodo.getName())
.startsWith("Fro")
.endsWith("do")
.isEqualToIgnoringCase("frodo")
.hasLength(5);
6. 숫자 검증
// 정확한 값 비교
assertThat(5.1).isEqualTo(5, withPrecision(1d));
// 범위 검증
assertThat(someNumber)
.isGreaterThan(0)
.isLessThanOrEqualTo(100);
7. 예외 검증
assertThat(exception)
.hasNoCause()
.hasMessageEndingWith("error")
.isInstanceOf(IllegalArgumentException.class);
8. Map 검증
Map<Integer, String> map = Map.of(1, "a", 2, "b");
assertThat(map)
.isNotEmpty()
.hasSize(2)
.containsKey(1)
.doesNotContainKeys(10)
.contains(entry(2, "b"));
고급 기능
1. 사용자 정의 오류 메시지
테스트가 실패했을 때 더 명확한 메시지를 제공할 수 있습니다:
assertThat(person.getAge())
.as("%s의 나이는 100세여야 합니다", person.getName())
.isEqualTo(100);
실패 시 출력:
[홍길동의 나이는 100세여야 합니다] expected:<100> but was:<34>
2. Java 8 람다 지원
// Java 7 방식
assertThat(fellowshipOfTheRing)
.filteredOn("race", HOBBIT)
.containsOnly(sam, frodo, pippin, merry);
// Java 8 람다 방식
assertThat(fellowshipOfTheRing)
.filteredOn(character -> character.getRace().equals(HOBBIT))
.containsOnly(sam, frodo, pippin, merry);
3. 체이닝을 통한 복합 검증
AssertJ의 가장 큰 장점 중 하나는 메서드 체이닝을 통해 여러 조건을 한 번에 검증할 수 있다는 것입니다:
assertThat(user)
.isNotNull()
.extracting("name", "email", "age")
.containsExactly("김철수", "kim@example.com", 25);
IDE 활용 팁
AssertJ는 IDE의 자동완성 기능과 완벽하게 호환됩니다. assertThat().을 입력하면 해당 타입에 맞는 수십 가지의 검증 메서드를 IDE가 제안해줍니다. 이는 테스트 작성 생산성을 크게 향상시켜줍니다.
왜 AssertJ를 사용해야 할까?
1. 가독성
- 자연어처럼 읽히는 테스트 코드
- 의도가 명확하게 드러나는 메서드 이름
2. 풍부한 API
- 각 데이터 타입에 특화된 검증 메서드 제공
- 복잡한 검증을 간단하게 표현
3. 우수한 오류 메시지
- 실패 시 명확하고 자세한 정보 제공
- 사용자 정의 메시지 지원
4. 확장성
- 기본 Java 타입 외에도 Guava, Joda Time 등 지원
- 사용자 정의 단언 생성 가능
🍪 실습 예제로 살펴보는 AssertJ
이론만으로는 부족하죠! 실제 테스트 코드를 통해 AssertJ를 어떻게 활용하는지 살펴보겠습니다.
1. 문자열 테스트
@Test
void replace() {
String actual = "abc".replace("b", "d");
assertThat(actual).isEqualTo("adc");
}
문자열 치환 결과를 검증하는 간단한 예제입니다. isEqualTo()로 기대값과 정확히 일치하는지 확인합니다.
2. 배열 테스트
@Test
void splitToArray() {
String[] actual = "1,2".split(",");
assertThat(actual).contains("1", "2");
}
@Test
void splitToOne() {
String[] actual = "1".split(",");
assertThat(actual).containsExactly("1");
}
- contains(): 배열이 특정 요소들을 포함하는지 확인 (순서 무관)
- containsExactly(): 배열이 정확히 해당 요소들만 포함하는지 확인 (순서 및 개수 중요)
3. 문자열 부분 추출 테스트
@Test
void splitToNumber() {
String actual = "(1,2)".substring(1, 4);
assertThat(actual).isEqualTo("1,2");
}
substring() 메서드로 괄호를 제거한 결과를 검증합니다.
4. 예외 상황 테스트
@Test
void charAtTest() {
assertThatThrownBy(() -> {
"abc".charAt(4); // 인덱스 범위 초과
}).isInstanceOf(StringIndexOutOfBoundsException.class);
}
AssertJ에서는 assertThatThrownBy()를 사용하여 예외 발생을 검증할 수 있습니다. try-catch 블록을 사용하지 않아도 됩니다!
5. Set 컬렉션 테스트
private Set<Integer> numbers;
@BeforeEach
void setUp() {
numbers = new HashSet<>();
numbers.add(1);
numbers.add(1); // 중복 - Set에서는 하나만 저장됨
numbers.add(2);
numbers.add(3);
}
@Test
void contains() {
assertThat(numbers.contains(1)).isTrue();
assertThat(numbers.contains(2)).isTrue();
assertThat(numbers.contains(3)).isTrue();
}
// 더 AssertJ스러운 방식
@Test
void containsWithAssertJ() {
assertThat(numbers)
.hasSize(3) // Set의 크기 확인 (중복 제거됨)
.contains(1, 2, 3)
.doesNotContain(4);
}
Set의 contains() 메서드 결과를 boolean으로 검증하는 것보다, AssertJ의 컬렉션 전용 메서드를 사용하는 것이 더 직관적입니다.
실습에서 배운 AssertJ 팁
- 예외 테스트는 assertThatThrownBy() 사용
- // 기존 방식 (권장하지 않음) try { riskyMethod(); fail("예외가 발생해야 함"); } catch (Exception e) { assertThat(e).isInstanceOf(RuntimeException.class); } // AssertJ 방식 (권장) assertThatThrownBy(() -> riskyMethod()) .isInstanceOf(RuntimeException.class) .hasMessageContaining("error");
- 컬렉션은 직접 검증하기
- // 덜 직관적 assertThat(list.contains(item)).isTrue(); // 더 직관적 assertThat(list).contains(item);
- @BeforeEach와 함께 사용하여 테스트 격리 각 테스트마다 독립적인 상태를 보장하여 안정적인 테스트를 작성할 수 있습니다.
마무리
AssertJ는 단순히 테스트 라이브러리를 바꾸는 것 이상의 의미를 갖습니다. 더 읽기 쉽고, 유지보수하기 좋은 테스트 코드를 작성할 수 있게 해주어 전체적인 코드 품질 향상에 기여합니다.
다음에 테스트 코드를 작성할 때는 AssertJ를 사용해보세요. 한 번 사용해보시면 기존 assert 방식으로 돌아가기 어려우실 겁니다!
더 자세한 내용은 AssertJ 공식 문서를 참고하세요.
'Project · Etc > Project' 카테고리의 다른 글
[ Project ]Spring Boot - war 프로젝트 배포 및 실행 (0) | 2023.10.20 |
---|---|
[ Project ] 네이버 클라우드 플랫폼(NCP) 가입 및 프로젝트 배포 (1) | 2023.10.18 |
[ Project · ArtMarket ] X-Frame-Options 트러블 슈팅 (0) | 2023.10.14 |
[ Project · ArtMarket ] 채팅 기능 사용 기술 정리 - WebSocket · Stomp · sockJs (0) | 2023.10.14 |
[ Project · ArtMarket ] 알림 · 파일 기능 분석과 DB 설계 (0) | 2023.10.06 |