개발환경
IDE : intelliJ
FrameWork : springboot 3.x
Launguage : java 17
TestTool : Junit5
HamcrestMatcher 사용하기
Hamcrest 라이브러리는 테스트 프레임워크는 아니지만, 간명한 매치 규칙을 선언하는데 도움을 준다. 매치 규칙은 다양한 상황에 쓰일 수 있지만 특히 단위 테스트에서 유용하게 쓰일 수 있다.
매처(Matcher)는 제약(constraint) 혹은 술어(predicate)로 표현되는데 이것을 사용하여 테스트 메서드 안에 있는 복잡한 단언문을 간명하게 처리할 수 있다.
아래는 중복 조건을 검사하는 단언문을 assertThat을 통해 처리하는 예제이다.
@Test
@DisplayName("Hamcrest를 사용하지 않은 테스트")
public void testWithoutHamcrest() {
assertEquals(3, values.size());
assertTrue(values.contains("Oliver") || values.contains("Jack") || values.contains("Harry"));
}
@Test
@DisplayName("Hamcrest를 사용해서 자세한 실패 정보를 나타내는 테스트")
public void testListWithHamcrest() {
assertThat(values, hasSize(3));
assertThat(values, hasItem(anyOf(equalTo("Oliver"), equalTo("Jack"), equalTo("Harry")))); // Matcher를 사용해서 간명하게 표시
}
사실 assertThat을 쓰냐, 단언문을 여러 개로 쓰냐는 단순히 개발자의 선택이다. 본인이 좀 더 작성하기 편하고, 팀원과 공유할 때 가독성 있는 코드를 보다 편하고 쉽게 전달할 수 있는 방법을 선택하면 된다.
이전 글에도 짧게 언급했는데, Junit5에서는 더 이상 assertThat 메서드를 제공하지 않는다. 따라서 별도로 org.hamcrest.MatcherAssert를 import 해서 사용해야 한다.
MatcherAssert에서 제공하는 assertThat에서는 다음과 같이 두 세가지 파라미터를 사용할 수 있다.
- 단언문이 실패했을 때 나타나는 오류 메시지(선택사항)
- 실제 값 또는 실제 객체
- 예상 값에 대한 Matcher 객체
//assertThat(실제 객체, 예상값에 대한 Matcher 객체)
assertThat(values, hasItem(anyOf(equalTo("Oliver"), equalTo("Jack"), equalTo("Harry")))); // Matcher를 사용해서 간명하게 표시
Matcher 객체를 생성하려면 org.hamcrest.Matchers 클래스에서 제공하는 정적 팩토리 메서드 중 하나를 사용해야 한다.
- 일반적인 Hamcrest 정적 팩토리 메서드의 사용 사례
팩터리 메서드 | 사용 사례 |
anything | 아무것이나 일치하면 될 때 사용한다. 단언문을 더 읽기 쉽게 만들 떄 유용하다. |
is | 문장의 가독성을 높이고 싶을 때 사용한다. 일종의 장식 표현이다. |
allOf | 모든 매체 규칙을 만족하는지 확인한다. ( &&연산자와 유사하다.) |
anyOf | 하나라도 일치하는 매치 규칙이 있는지 확인한다. ( ||연산자와 비슷하다.) |
not | 매치 규칙의 의미를 뒤집는다 ( ! 연산자와 비슷하다.) |
instanceOf | 객체가 특정 클래스의 인스턴스인지 확인한다. |
sameInstance | 객체 동일성을 확인한다. |
nullValue, notNullValue | null이 null이 아닌지 확인한다. |
hasProperty | 객체가 특정 속성을 가졌는지 확인한다. |
hasEntry, hasKey, hasValue | 맵이 특정 엔트리, 키, 값을 가졌는지 확인한다. |
hasItem, hasItems | 컬렉션이 특정 요소나 요소들을 가졌는지 확인한다. |
closeTo, GreaterThan, GreaterThanOrEqualTo, lessThan, lessThanOrEqualTo | 주어진 숫자가 가까운지, 큰지, 크거나 같은지, 작은지, 작거나 같은지 확인한다. |
equalToIgnoringCase | 대소문자를 무시하고 주어진 문자열이 일치하는지 확인한다. |
equalToIgnoringWhiteSpace | 공백을 무시하고 주어진 문자열이 일치하는지 확인한다. |
containString, startsWith, endsWith | 주어진 문자열이 특정 문자열을 포함하는지, 특정 문자열로 시작하는지, 특정 문자열로 끝나는지 확인한다. |
HamcrestMatcher과 AssertJ 비교
그런데, 나는 HamcrestMatcher을 사용하지 않는다. ( 생각보다 불편하다! ) 대신 assertJ에서 제공하는 assertThat을 쓴다.
둘이 기능은 똑같다. 단지 쓰는 함수명과 문법 차이만 있을 뿐이다.
Hamcrest (org.hamcrest.Matcher)
- 표현력: Hamcrest는 매처(matchers)를 사용하여 조건을 정의합니다. 이 매처는 주로 정적인 메서드 체인으로 작성됩니다. 예를 들어, assertThat(value, is(equalTo(expectedValue)))와 같은 형태입니다.
- 확장성: 사용자가 직접 커스텀 매처를 쉽게 만들 수 있습니다.
- 제약적 표현: Hamcrest는 구문과 어휘가 제한적이어서 복잡한 검증 로직을 작성하기가 어려울 수 있습니다.
- 통합성: JUnit과 잘 통합되어 있어 널리 사용됩니다.
AssertJ
- 표현력: AssertJ는 플루언트 인터페이스를 제공하여 더 자연스럽고 읽기 쉬운 검증을 할 수 있습니다. 예를 들어, assertThat(value). isEqualTo(expectedValue)와 같이 작성할 수 있습니다.
- 확장성: AssertJ도 사용자 정의 검증을 쉽게 만들 수 있으며, Hamcrest보다 더 직관적입니다.
- 풍부한 기능: AssertJ는 컬렉션, 맵, 예외, 제네릭 타입 등 다양한 객체 타입에 대한 풍부한 검증 메서드를 제공합니다.
- 인간 친화적: 에러 메시지가 더 읽기 쉽고 유용한 정보를 제공합니다.
간단한 비교 예시를 보자
- Hamcrest
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
assertThat(actualValue, is(equalTo(expectedValue)));
assertThat(list, hasSize(3));
assertThat(map, hasKey("key1"));
- AssertJ
import static org.assertj.core.api.Assertions.assertThat;
assertThat(actualValue).isEqualTo(expectedValue);
assertThat(list).hasSize(3);
assertThat(map).containsKey("key1");
둘이 검증하는 것은 똑같지만 사용방법만 다른 것을 알 수 있다. 일반적으로 내 주변에는 주로 AssertJ를 좀 더 많이 쓰는 것 같다.
assertThat을 사용하면 static import시 여러 개가 뜨는데 본인이 사용할 패키지를 알맞게 선택해서 쓰도록 하자.
'Dev > TDD' 카테고리의 다른 글
Junit In Action - TDD를 위한 테스트 원칙, 도구 및 활용 Review - Junit4와 Junit5 비교 (0) | 2024.06.26 |
---|---|
Junit In Action - TDD를 위한 테스트 원칙, 도구 및 활용 Review - Junit 5 아키텍처 개요 ( Junit4와 어떻게 달라졌을까? ) (2) | 2024.06.14 |
Junit In Action - TDD를 위한 테스트 원칙, 도구 및 활용 Review - 핵심 애노테이션 ( 1/2 ) (1) | 2024.06.14 |
MockMvc 을 사용해서 Controller 테스트 코드 작성하기 (1) | 2024.01.29 |
@Transactional 사용시 Insert/update 유의점 (0) | 2024.01.29 |