# AssertJ

AssertJ는 테스트 라이브러리이다.

* 메소드 체이닝을 지원하기 때문에 좀 더 깔끔하고 읽기 쉬운 테스트 코드 작성 가능(가독성이 좋음)
* 개발자가 테스트를 하면서 필요하다고 상상할 수 있는 거의 모든 메소드를 제공

## 의존성

![image-20211226210215502](https://799616298-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M26jG1oNlY_ZSgBz2cM-887967055%2Fuploads%2Fgit-blob-8579536f57f693163ad626caad0446b22a94ec3f%2Fimage-20211226210215502.png?alt=media)

`spring-boot-starter-test`에 기본적으로 포함되어있다. `spring-boot-starter-test`를 사용하지 않고 추가하는 경우에는 다음과 같이 추가할 수 있다.

### Gradle

```groovy
testCompile 'org.assertj:assertj-core:3.21.0'
```

### Maven

```xml
<dependency>
  <groupId>org.assertj</groupId>
  <artifactId>assertj-core</artifactId>
  <version>3.21.0</version>
  <scope>test</scope>
</dependency>
```

## 테스트 코드 작성 시작하기

### Assertions.assertThat()

모든 테스트 코드는 `assertThat()` 메서드에서 시작한다.

```java
assertThat(테스트 타겟)
    .method1()
    .method2()
    .method3();
```

### 테스트 실패 메세지

* 테스트 실패시 나타낼 메세지를 `.as()`로 표현할 수 있다.
* 반드시 검증 문 앞에 작성되어야한다.

```java
    @Test
    void asExample() {
        String blank = " ";
        assertThat(StringUtils.hasText(blank))
                .as("check '%s' is null", blank)
                .isTrue();
    }
```

다음과 같이 작성해두면 테스트 실패시 콘솔창에 에러메세지가 다음과 같이 출력된다.

```
[check ' ' is null] 
Expecting value to be true but was false
org.opentest4j.AssertionFailedError: [check ' ' is null] 
```

다음과 같이 검증문 뒤에 작성할 경우 호출되지 않는 것을 볼 수 있다.

```java
    @Test
    void asExample() {
        String blank = " ";
        assertThat(StringUtils.hasText(blank))
                .isTrue()
                .as("check '%s' is null", blank);
    }
```

```
Expecting value to be true but was false
org.opentest4j.AssertionFailedError: 
Expecting value to be true but was false
```

### Collection 필터링

#### .filteredOn()

`.filteredOn()`으로 테스트할 데이터들을 필터링 할 수 있다.

```java
    @Test
    void test() {
        EnumSet<DayOfWeek> days = EnumSet.allOf(DayOfWeek.class);
        assertThat(days)
                .filteredOn(d -> d.getValue() > 4)
                .containsOnly(DayOfWeek.FRIDAY, DayOfWeek.SATURDAY, DayOfWeek.SUNDAY);
    }
```

이때 `not`, `in`, `notIn`에 대한 객체 프로퍼티를 검증할 수 도 있다.

```java
        List<Human> list = new ArrayList<>();
        list.add(new Human("faker", 26));
        list.add(new Human("bang", 26));
        list.add(new Human("wolf", 26));
        list.add(new Human("keria", 20));

        assertThat(list)
                .filteredOn("age", not(26))
                .containsOnly(list.get(3));
```

### Collection Data Extracting

기존의 클래스의 특정 데이터를 비교하기 위해서는 별도 리스트를 만들어 다음과 같이 테스트를 진행해야했다.

```java
@Test
void no_extracting() throws Exception{
    List<String> names = new ArrayList<>();
    for (T1 member : members) {
        names.add(member.getName());
    }

    assertThat(names).containsOnly("faker", "bang", "wolf", "keria");
}
```

하지만 assertj에서 `extracting`을 지원해주면서 필요한 데이터만 추출해 테스트가 가능해졌다.

```java
    @Test
    void extracting() {
        List<T1> members = new ArrayList<>();
        members.add(new T1("faker", 26));
        members.add(new T1("bang", 26));
        members.add(new T1("wolf", 26));
        members.add(new T1("keria", 20));

        assertThat(members)
                .extracting("name")
                .contains("faker", "bang", "wolf", "keria");


        assertThat(members)
                .extracting("name", "age")
                .contains(tuple("faker", 26),
                        tuple("bang", 26),
                        tuple("wolf", 26),
                        tuple("keria", 20));
    }
```

추출할 데이터가 한개라면 타입 지정이 가능하며, 여러개인 경우에는 assertj에서 지원해주는 `tuple()`로 테스트할 수 있다.

### SoftAssertions

`Assertions.assertThat()`은 테스트가 한개라도 실패하면 해당 테스트는 중지된다.

`SoftAssertions`을 이용하면 모든 assertThat을 실행하고 해당 실패 내역을 확인할 수 있다.

```java
    @Test
    void test() {
        String str = "test";
        SoftAssertions.assertSoftly(softAssertions -> {
            softAssertions.assertThat(StringUtils.hasText(str))
                    .as("check '%s' is not null", str)
                    .isFalse();

            softAssertions.assertThat(StringUtils.hasLength(str))
                    .as("check '%s' is not null", str)
                    .isTrue();
        });
    }
```

```
Multiple Failures (1 failure)
-- failure 1 --
[check 'test' is not null] 
Expecting value to be false but was true
at SoftAssertionTest.lambda$test$0(SoftAssertionTest.java:15)
org.assertj.core.error.AssertJMultipleFailuresError: 
Multiple Failures (1 failure)
-- failure 1 --
```

## 참고

* <https://sun-22.tistory.com/86>
