AssertJ Methods

원태연·2023년 2월 27일
0
post-thumbnail

AssertJ

AssertJ는 더 다양한 assertion을 제공하는 자바 라이브러리이다.
공식 문서 : https://assertj.github.io/doc/

Junit5에서 제공하는 Assertion method가 존재하는 데, AssertJ는 어떤 특징 때문에 추가적인 라이브러리를 사용하는 것일까?

특징

  • 메서드 체이닝을 지원하여 더 직관적이고 읽기 쉬운 테스트코드를 작성
  • 정확한 에러메세지
  • JUnit이외의 추가적인 메서드 지원

AssertJ의 여러 메소드들을 연쇄적으로 호출해 코드를 작성할 수 있다. (메서드 체이닝)

import static org.assertj.core.api.Assertions.assertThat; 

import org.junit.jupiter.api.Test;

public class SimpleAssertionsExample {
  @Test
  void a_few_simple_assertions() {
    assertThat("The Lord of the Rings").isNotNull()   
                                       .startsWith("The") 
                                       .contains("Lord") 
                                       .endsWith("Rings"); 
  }
}

다양한 검증 메서드

객체 검증

가장 기본적인 검증 메서드이다.

@Test
void test() {
    String value = "Royce";
    assertThat(value).isEqualTo("Royce");
}

이름에서도 유추할 수 있듯이, equals() 연산을 활용한다.
만약, equals를 재정의 하지 않은 객체의 동등성을 비교하고자 한다면 isEqualToComparingFieldByFieldRecursively() 를 사용할 순 있다. 근데 deprecated 되었으니, 아래와 같은 방식usingRecursiveComparison() 으로 좀더 유연하게 필드값을 활용하는 검증 메서드를 사용할 수 있다.

@Test
void test() {
    Person value = new Person(20, "Royce");

//  Deprecated
//  assertThat(value).isEqualToComparingFieldByFieldRecursively(new Person(20, "Royce"));

    assertThat(value).usingRecursiveComparison()
                .isEqualTo(new Person(20, "Royce"));

}

객체 검증에 대해 다양한 assertions을 제공한다. 메서드 체이닝을 활용하여 더 다양하고 구체적인 테스트를 고려해보자

@Test
void test() {
    Person value = new Person(20, "Royce");
    
    assertThat(value).isNotNull()
            .isExactlyInstanceOf(Person.class)
            .usingRecursiveComparison()
            .isNotEqualTo(new Person(22, "Pooh"));
}

## 리스트/배열 검증
@Test
void test() {
    List<String> list = List.of("A", "B", "C", "D");
    assertThat(list)
            .isNotNull()
            .doesNotContainNull()
            .doesNotContain("E")
            .containsSequence("A", "B")  // 순서 검증
            .doesNotContainSequence("B", "A")
            .startsWith("A")
            .endsWith("C", "D")
            .hasSize(4)
            .containsOnlyOnce("A") // "A"가 한개만 존재하는지 검증
            .contains("D");
}

extracting 활용하기

객체 배열에 대하여 값을 테스트 하고 싶다면 extracting()을 활용할 수 있다. 해당 객체의 필드 이름을 알아야 한다는 단점이 있지만, 테스트만을 위한 getter를 추가하지 않아도 테스트할 수 있는 장점도 존재한다.

@Test
void test() {
    List<Person> list = List.of(new Person("Royce"), new Person("Pooh"), new Person("Luka"));
    assertThat(list).extracting("name")
            .isNotNull()
            .doesNotContainNull()
            .doesNotContain("Jerry")
            .containsSequence("Royce", "Pooh")
            .startsWith("Royce")
            .hasSize(3)
            .contains("Luka")
            .doesNotHaveDuplicates(); // 중복 체크
    
}

하나의 필드만 추출하는 케이스만 제공하진 않는다. tuple을 활용하여 다양한 필드에 대한 검증도 지원한다

tupleorg.assertj.core.api.Assertions.tuple에 존재합니다.

@Test
void test() {
    List<Person> list = List.of(
        new Person("Royce", 20), 
        new Person("Pooh", 21), 
        new Person("Luka", 22)
    );
    
    assertThat(list).extracting("name", "age")
            .isNotNull()
            .doesNotContainNull()
            .doesNotContain(tuple("Royce", 123))
            .startsWith(tuple("Royce", 20))
            .hasSize(3)
            .contains(tuple("Royce", 20), tuple("Pooh", 21));
}

filteredOn 활용하기

@Test
void test() {
    List<Person> list = List.of(
            new Person("Royce", 20),
            new Person("Pooh", 21),
            new Person("Luka", 22)
    );

    assertThat(list).filteredOn("age", 20)  //age필드 값이 20인 조건으로 필터
            .hasSize(1)
            .extracting("name") // 이름 추출
            .containsOnly("Royce");
}
@Test
void test() {
    List<Person> list = List.of(
            new Person("Royce", 20),
            new Person("Pooh", 21),
            new Person("Luka", 22)
    );

    assertThat(list).filteredOn(person -> person.getAge() > 20) // 람다를 넣을 수 있다
            .hasSize(2)
            .extracting("name")
            .containsOnly("Pooh", "Luka");
}

## 예외 검증 예상된 상황에서 예상된 예외를 던지는에 대한 검증도 지원한다
@Test
void test() {
    Throwable thrown = AssertionsForClassTypes.catchThrowable(() -> {
        throw new Exception("boom!");
    });

    assertThat(thrown).isInstanceOf(Exception.class)
            .hasMessageContaining("boom");
}

위 코드와 같이 직접 Throwable 객체를 생성할 수 있지만, assertThatThrownBy() 를 통해 예외 객체를 생성하지 않고 좀 더 가독성 높은 테스트 코드를 구성할 수 있다.

@Test
void test() {
    assertThatThrownBy(() -> {
        throw new Exception("boom!");
    }).isInstanceOf(Exception.class)
            .hasMessageContaining("boom");
}

+ 추가적으로, Java의 표준 예외를 위한 메서드도 제공한다.

  • assertThatNullPointerException
  • assertThatIllegalArgumentException
  • assertThatIllegalStateException
  • assertThatIOException
    지정된 표준예외 이외의 예외가 발생하면 테스트가 실패한다.
@Test
void test() {
    assertThatNullPointerException().isThrownBy(() -> {
        throw new NullPointerException("NPE!");
    }).withMessage("NPE!");
}

## Map 검증
@Test
void test() {
    Map<String, String> results = new HashMap<>();
    results.put("Royce", "10000원");
    results.put("Pooh", "꽝");
    results.put("Luca", "50000원");

    assertThat(results)
            .hasSize(3)
            .containsEntry("Royce", "10000원") // Map에 존재하는 entry 검증
            .extractingByKey("Royce") // Key값으로 get
            .isEqualTo("10000원");
}

나머지..

Predicate instance assertions

@Test
void test() {
    Predicate<Integer> predicate = power -> power > 4;
    assertThat(predicate)
            .accepts(5, 6, 7, 8)
            .rejects(1, 2, 3);
}

> Integer asserrtions
@Test
void test() {
    int a = 20;
    assertThat(a)
            .isBetween(19, 20)
            .isStrictlyBetween(19, 21)
            .isLessThanOrEqualTo(20)
            .isEven();
}

이 외에도 다양한 검증 메서드를 제공한다. 잘 찾아보고,, 잘 활용해보자

profile
앞으로 넘어지기

0개의 댓글