minlog
728x90

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 팁

  1. 예외 테스트는 assertThatThrownBy() 사용
  2. // 기존 방식 (권장하지 않음) try { riskyMethod(); fail("예외가 발생해야 함"); } catch (Exception e) { assertThat(e).isInstanceOf(RuntimeException.class); } // AssertJ 방식 (권장) assertThatThrownBy(() -> riskyMethod()) .isInstanceOf(RuntimeException.class) .hasMessageContaining("error");
  3. 컬렉션은 직접 검증하기
  4. // 덜 직관적 assertThat(list.contains(item)).isTrue(); // 더 직관적 assertThat(list).contains(item);
  5. @BeforeEach와 함께 사용하여 테스트 격리 각 테스트마다 독립적인 상태를 보장하여 안정적인 테스트를 작성할 수 있습니다.

마무리

AssertJ는 단순히 테스트 라이브러리를 바꾸는 것 이상의 의미를 갖습니다. 더 읽기 쉽고, 유지보수하기 좋은 테스트 코드를 작성할 수 있게 해주어 전체적인 코드 품질 향상에 기여합니다.
다음에 테스트 코드를 작성할 때는 AssertJ를 사용해보세요. 한 번 사용해보시면 기존 assert 방식으로 돌아가기 어려우실 겁니다!


더 자세한 내용은 AssertJ 공식 문서를 참고하세요.

728x90
profile

minlog

@jimin-log

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!