IntelliJ에서 테스트코드를 작성할 때, Assertions 키워드를 작성하면 자동완성 기능으로 두 가지를 추천해준다.
하나는 org.junit.jupiter.api.Assertions
이고, 하나는 org.assertj.core.api.Assertions
이다.
강의를 듣거나 다른 사람들의 코드들을 보면, 보동 후자를 사용하곤 한다.
심지어 JUnit5 공식 문서에 가도 서드파티 라이브러리인 AssertJ 사용을 권장한다.
그러면, 왜 다들 AssertJ를 쓰는걸까?
1. 가독성
다음 두 가지 메서드를 보아라.
// 1.
assertEquals(a, b);
// 2.
assertThat(a).isEqualTo(b);
1번 코드를 보면 어느것이 실제 값이고, 어느 것이 예상 값인지 쉽게 유추할 수 없다.
하지만 2번 코드에선 a가 실제 값이고, b가 예상값임을 쉽게 유추가능하다.
1번이 JUnit에서 제공하는 메서드이고, 2번이 AssertJ에서 제공하는 메서드이다.
AssertJ의 메서드는 왼쪽에서 오른쪽으로 자연스럽게 읽힌다.
이와 같이 가독성이 좋지 않은 JUnit 메서드를 읽기 좋게 풀어낸 것이 AssertJ이다.
2. 자세한 실패 메시지로 실패 원인 파악 용이
JUnit
// JUnit
assertTrue(name.contains("o"));
위 테스트를 돌렸을 때, 실패 메세지를 확인해보면 다음과 같다.
expected: <true> but was: <false>
Expected :true
Actual :false
이 실패 메세지를 통해서는 예상값이 true이지만 실제 값은 false라는 사실만 알 수 있고, 무슨 테스트를 실패한건지 즉, name이 "o"를 포함하지 않아서 실패했다는 사실은 알 수 없다.
AssertJ
해당 테스트 메서드를 AssertJ를 활용해 바꿔보자.
// AssertJ
assertThat(name).contains("o");
위 테스트를 돌렸을 때 실패 메세지는 다음과 같다.
java.lang.AssertionError:
Expecting actual:
"ash"
to contain:
"o"
실패 메세지만 보고도 어느 것을 테스트하려 했는데 실패한건지, 실패 원인이 무엇인지 파악할 수 있다.
테스트 실패 메세지가 떴을 때 어떤 테스트를 왜 실패했는지 빠르게 파악하는게 아주 중요하다.
조금 더 자세한 예시를 보자.
Players players = new Players("애쉬", "스플릿", "아코", "히이로", "비버", "마코");
List<String> winners = players.findWinners();
...
assertThat(winners).containsExactlyInAnyOrder("애쉬", "비버");
위의 테스트는 여섯명의 플레이어중 승자를 찾는 테스트이다.
그리고 작성자는 애쉬와 비버가 우승할 것이라고 예상했다.
하지만 아래와 같은 에러 메세지를 마주했다.
java.lang.AssertionError:
Expecting actual:
["애쉬", "스플릿"]
to contain exactly in any order:
["애쉬", "비버"]
elements not found:
["비버"]
and elements not expected:
["스플릿"]
위의 메세지를 해석해보면,
- 실제 우승자는
애쉬
와스플릿
이다. - 우리가 예상한 우승자는
애쉬
와비버
이다. 비버
는 우승자에 포함되지 못했다.스플릿
은 우승자가 아닐 줄 알았는데 우승자에 포함됐다.
이 네가지 사실을 알 수 있다.
이렇게 AssertJ를 사용하면 자세한 살패 메세지를 통해, 실패 원인을 보다 정확하게 파악할 수 있다.
3. 다양한 검증 메서드 제공으로 인한 편리함
위의 승자 판별 테스트를 JUnit을 사용해 테스트해보자.
// JUnit
assertTrue(winners.containsAll(List.of("애쉬", "스플릿")) && winners.size() == 2);
assertArrayEquals(winners.toArray(), new String[]{"애쉬", "스플릿"});
assertTrue(winners.containsAll(List.of("애쉬", "스플릿")));
// AssertJ
assertThat(winners).containsExactlyInAnyOrder("애쉬", "스플릿");
assertThat(winners).containsExactly("애쉬", "스플릿");
assertThat(winners).contains("애쉬", "스플릿");
위의 예시만 봐도 AssertJ를 써야하는 이유를 알 수 있다.
JUnit은 간단한 메서드밖에 제공해주지 않아서, 비교를 위해 많은 작업들이 필요시된다.
(ex. assertEquals, assertNotEquals, assertSame, assertTrue, assertNull, ...)
AssertJ는 다양한 메서드를 제공해줌으로써, 우리가 할 일을 덜어준다.
통일성도 있고, 문장처럼 읽혀서 가독성도 좋다.
정말 다양한 메소드를 제공하니, 어느 메서드를 사용할지 모르겠을 땐 assertThat(XXX)뒤에 .
을 찍어보고 IDE가 보여주는 메서드 리스트를 보며 보여주는 메서드를 골라 사용하면 된다.
4. 메서드 체이닝
AssertJ는 메서드 체이닝을 제공한다.
assertThat("[TITLE] Hello, My Name is ash")
.isNotEmpty()
.contains("[TITLE]")
.containsOnlyOnce("ash")
.doesNotStartWith("[ERROR]");
assertThat(score)
.isPositive()
.isGreaterThan(60)
.isLessThanOrEqualTo(75);
이와 같이, 메서드 체이닝을 통해 여러 테스트를 한 번에 할 수 있다.
위 테스트들을 JUnit이 기본으로 제공하는 메소드들을 사용해서 테스트한다고하면..
코드는 정말 길어질것이고, 짜는 사람도 보는 사람도 싫은 코드가 될 것이다!
이 외에도 AssertJ를 사용해야하는 이유는 정말로 많다.
앞으로는 AssertJ를 사용하는 이유를 알고 사용하도록 하자!
'프로그래밍' 카테고리의 다른 글
[Java] LinkedHashMap (0) | 2023.03.13 |
---|---|
[Java] 함수형 인터페이스와 람다 (5) | 2023.03.12 |
[Java] 복사와 불변 (new, unmodifiable, copyOf) (2) | 2023.02.24 |
[Java] 동일성(==)과 동등성(equals) (8) | 2023.02.23 |
JUnit5란? (0) | 2023.02.20 |