1. AssertJ
JUnit에서 제공하는 assert() 메소드의 부족한 표현력을 충족시켜줌
타입별로 다양한 검증 메소드를 제공하여 테스트 코드를 더욱 쉽게 작성할 수 있게 도와줌
JUnit assert() 와 AssertJ 비교
id가 "a"를 포함하고 있는 지 여부 검사
// JUnit assert()사용 코드
assertTrue(id.contains("a"));
// 단언 실패 메시지, 실패 이유를 알 수 없음
org.opentest4j.AssertionFailedError: expected: <true> but was: <false>
// AssertJ 사용 코드, id가 "a" 를 포함하는 지 여부를 검사한다는 것을 직관적으로 확인할 수 있음
assertThat(id).contains("a");
// 단언 실패 메시지, "bcd"가 "a"를 포함할거라고 기대했지만 그렇지 않다라는 것을 알려줌
java.lang.AssertionError:
Expecting:
<"bcd">
to contain:<"a">
2. 의존 설정
testImplementation group: 'org.assertj', name: 'assertj-core', version: '3.23.1'
3. 사용법
assertThat(실제값).검증메소드(기대값);
// 여러 검증메소드를 검증할 수 있음
assertThat(실제값)
.검증메소드1(기대값)
.검증메소드2(기대값)
.검증메소드3(기대값);
4. assertThat() 메소드
1) 기본 검증 메소드
- 모든 타입에 사용할 수 있는 검증 메소드
@Test
@DisplayName("검증 메소드 사용법")
public void basicTest() {
assertThat("Hello, world! Nice to meet you.") // 주어진 "Hello, world! Nice to meet you."라는 문자열은
.isEqualTo("Hello, world! Nice to meet you.")// "Hello, world! Nice to meet you."과 일치합니다.
.isNotEmpty() // 비어있지 않고
.contains("Nice") // "Nice"를 포함하고
.contains("world") // "world"도 포함하고
.doesNotContain("ZZZ") // "ZZZ"는 포함하지 않으며
.startsWith("Hell") // "Hell"로 시작하고
.endsWith("u.") // "u."로 끝나며
;
}
- isEqualTo(값) : 값과 같은지 검증한다.
- isNotEqualTo(값) : 값과 같지 않은지 검증한다.
- isNull() : null인지 검증한다
- isNotNull() : null이 아닌지 검증한다.
- isIn(값 목록) : 값 목록에 포함되어 있는지 검증한다.
- isNotIn(값 목록)
*isIn(), isNotIn()의 값 목록은 가변 인자로 주거나 List와 타입(정확히는 Iterable을 구현하는 타입)을 이용해서 전달
- Comparable 인터페이스를 구현한 타입이나 int, double과 같은 숫자 타입의 경우 사용할 수 있는 검증 메소드
- isLessThan(값) : 값 보다 작은지 검증한다.
- isLessThanOrEqualTo(값) : 값보다 작거나 같은지 검증한다.
- isGreaterThan(값) : 값 보다 큰지 검증한다.
- isGreaterThanOrEqualTo(값) : 값보다 크거나 같은지 검증한다.
- isBetween(값1, 값2) : 값1과 값2 사이에 포함되는지 검증한다.
2) String에 대한 추가 검증 메서드
@Test
@DisplayName("String 타입, 특정값 포함 검증 메소드")
public void stirngTypeTest() {
assertThat("String Type Assert") // 실제값
.contains("String Type Assert") // 문자열 전체를 포함하는지 검증
.contains("String") // 문자열 일부를 포함하는지 검증
.contains(" ") // 문자열의 일부 중 공백을 포함하는지 검증
.containsOnlyOnce("String")
.containsWhitespaces()
// .containsOnlyDigits()
// .containsOnlyWhitespaces()
;
}
- contains(CharSequance... values) : 인자로 지정한 문자열들을 모두 포함하고 있는지 검증한다.
- containsOnlyOnce(CharSequance sequance) : 해당 문자열을 딱 한 번만 포함하는지 검증한다.
- containsOnlyDigits() : 숫자만 포함하는지 검증한다.
- containsWhitespaces() : 공백 문자를 포함하고 있는지 검증한다.
- containsOnlyWhitespaces() : 공백 문자만 포함하는지 검증한다.
- 특정 값을 포함하지 않는지 검사하는 메소드
@Test
@DisplayName("String 타입, 특정값 미포함 검증 메소드")
public void stirngType2Test() {
assertThat("String Type Assert") // 실제값
.doesNotContain("String Type2 Assert") // 문자열 전체 미포함 검증, 이 문자열을 포함하지 않지?
// .doesNotContainAnyWhitespaces() // 공백 미포함 검증, 문자열에 공백이 없지?
.doesNotContainOnlyWhitespaces() // 공백만 미포함 검증, 공백만 있는 문자열 아니지?
.doesNotContainPattern("^[가-힣]*$") // 정규표현식 미포함 검증, 한글을 포함하고 있니? , pattern(): 컴파일된 정규표현식을 String 형태로 반환
.doesNotContainPattern(Pattern.compile("St...n")); // 정규표현식 미포함 검증, compile(regex)메소드 사용: 주어진 정규표현식으로 패턴을 만든다
}
- doesNotContain(CharSequence... values) : 인자로 지정한 문자열들을 모두 포함하고 있지 않은지 검증한다.
- doesNotContainAnyWhitespaces() : 공백 문자를 포함하고 있지 않은지를 검증한다.
- doesNotContainOnlyWhitespaces() : 공백 문자만 포함하고 있지 않은지를 검증한다.
- doesNotContainPattern(Pattern pattern) : 정규 표현식에 일치하는 문자를 포함하고 있지 않은지를 검증한다.
- doesNotContainPattern(CharSequence pattern) : 정규 표현식에 일치하는 문자를 포함하고 있지 않은지를 검증한다.
- startsWith(CharSequance prefix) : 지정한 문자열로 시작하는지를 검증한다.
- doesNotStartWith(CharSequence prefix) : 지정한 문자열로 시작하지 않는지를 검증한다.
- endsWith(CharSequence suffix) : 지정한 문자열로 끝나는지를 검증한다.
- doesNotEndWith(CharSequence suffix) : 지정한 문자열로 끝나지 않는지를 검증한다.
3) 숫자에 대한 추가 검증 메소드
- isZero() / isNotZero() : 0인지 또는 0이 아닌지를 검증한다.
- isOne() : 1 인지를 검증한다.
- isPositive() / isNotPositive() : 양수인지 또는 양수가 아닌지를 검증한다.
- isNegative() / isNotNegative() : 음수인지 또는 음수가 아닌지를 검증한다.
LocalDateTime regDt = user.getRegDt();
assertThat(regDt).ifAfter(LocalDateTime.of(2022, 11, 30, 23, 59, 59));
- 날짜와 시간을 비교하는 주요 메소드
- isBefore(비교할 값) : 비교할 값보다 이전인지 검증한다.
- isBeforeOrEqualTo(비교할 값) : 비교할 값보다 이전이거나 같은지 검증한다.
- isAfter(비교할 값) : 비교할 값보다 이후인지 검증한다.
- isAfterOrEqualTo(비교할 값) : 비교할 값보다 이후이거나 같은지 검증한다.
- LocalDateTime, OffsetDateTime, ZonedDateTime 타입에 대한 추가 검증 메소드
- isEqualToIgnoringSeconds(비교할 값) : 초 이하 시간을 제외한 나머지 값이 같은지 검증한다. 즉 분 단위까지 값이 같은지 검증한다.
- isEqualToIgnoringMinutes(비교할 값) : 분 이하 시간을 제외한 나머지 값이 같은지 검증한다. 즉 시 단위까지 값이 같은지 검증한다.
- isEualToIgnoringHours(비교할 값) : 시 이하 시간을 제외한 나머지 값이 같은지 검증한다. 즉 일 단위까지 값이 같은지 검증한다.
5) 콜렉션에 대한 검증 메소드
- contains(E ... values) : 콜렉션이 지정한 값을 포함하는지 검증한다.
- containsOnly(E ... values) : 콜렉션이 지정한 값 중 일부를 포함하는지 검증한다.
- containsOnlyOnce(E ... values) : 콜렉션이 지정한 값을 한 번만 포함하는지 검증한다.
- containsKey(K Key) : Map이 지정한 키를 포함하는지 검증한다.
- containsKeys(K... Keys) : Map이 지정한 키들을 포함하는지 검증한다.
- containsOnlyKeys(K... Keys) : Map이 지정한 키만 포함하는지 검증한다.
- doesNotContainKeys(K... Keys) : Map이 지정한 키들을 포함하지 않는지 검증한다.
- containsValues(VALUE... values) : Map이 지정한 값들을 포함하는지 검증한다.
- contains(Entry<K,V> ... values) : Map이 지정한 Entry<K,V>를 포함하는지 검증한다.
6) 익셉션 관련 검증 메소드
- assertThatThrownBy() : 인자로 받은 람다에서 익셉션이 발생하는지 검증한다.
assertThatThrownBy(() -> new File("nofile.txt"));
- isInstanceOf() : 익셉션의 타입을 추가로 검증할 수 있다.
assertThatThrownBy(() -> new File("nofile.txt"));
.isInstanceOf(IOException.class);
- assertThatExceptionOfType() : 발생할 익셉션의 타입을 지정하고 isThrownBy() 메서드를 이용해서 익셉션이 발생할 코드 블록을 지정할 수 있다.
assertThatExceptionOfType(IOException.class)
.isThrownBy(() -> {
readFile(new File("nofile.txt"));
});
- assertThatIOExceptionOfType() : IOException이 발생하는지 검증한다.
assertThatIOExceptionOfType(IOException.class)
.isThrownBy(() -> {
readFile(new File("nofile.txt"));
});
// assertThatIOException() 메소드 외에
// assertThatNullPointerException()
// assertThatIllegalArgumentException()
// assertThatIllegalStateException() 메소드를 제공
- doesNotThrowAnyException() : 익셉션이 발생하지 않는 것을 검증한다.
assertThatCode(() -> {
readFile(new File("pom.xml"));
}).doesBotThrowAnyException();
5. SoftAssertions로 모아서 검증하기
JUnit 5의 assertAll()과 유사하다. 여러 검증을 한 번에 수행하고 싶을 때 사용한다.
@Test
@DisplayName("SoftAssertions")
public void softTest() {
SoftAssertions soft = new SoftAssertions();
soft.assertThat(1).isBetween(0, 2);
soft.assertThat(1).isGreaterThan(2); // 1이 2보다 큰지 검증하는 코드, 검증을 통과할 수 없지만 코드 실행 시점에는 검증 실패가 발생하지 않음
soft.assertThat(1).isLessThan(0);
soft.assertAll(); // 실제 검증은 assertAll()메소드를 실행할 때 진행
}
-> 실행결과에 검증하는 대상 중 두 개가 검증에 실패했다는 내용과 각 실패 대상 위치를 알려줌
- SoftAsserrtions.assertSoftly() 정적 메소드 : assertAll()메소드를 직접 호출하지 않아도 된다.
SoftAssertions.assertSoftly(soft -> {
soft.assertThat(1).isBetween(0, 2);
soft.assertThat(1).isGreaterThan(2);
soft.assertThat(1).isLessThan(0);
});
6. as()와 describedAs()로 설명 달기
- as(): 테스트에 설명을 붙인다. as()메소드로 지정한 설명 문구가 실패 메시지에 표시된다.
- as() 메소드 사용
@Test
@DisplayName("as()Test")
public void asTest() {
userRegister.register("id", "pw", "email");
User savedUser = fakeRepository.findById("id");
assertThat(savedUser.getId())
.as("ID 검사")
.isEqualTo("abc");
}
- as()메소드에는 문자열 포맷을 사용할 수 있음, 첫 번째 인자는 포맷팅을 포함한 문자열, 두 번째 인자부터는 포맷팅에 사용할 값 전달
@Test
@DisplayName("as()Test")
public void asTest2() {
userRegister.register("id", "pw", "email");
User savedUser = fakeRepository.findById("id");
assertThat(savedUser.getId())
.as("ID 검사: %s", "abc")
.isEqualTo("abc");
}
- 한 테스트에서 다수의 검증 메소드를 실행할 때 as()를 유용하게 사용할 수 있음, 검증 실패하는 단언이 있을 시 ret[] 으로 몇 번째 값이 검증에 실패했는지 알 수 있음
List<Integer> ret = getResults();
List<Integer> expected = Arrays.asList(1, 2, 3);
SoftAssertions soft = new SoftAssertions();
for (int i = 0 ; i < expected.size() ; i++) {
soft.assertThat(ret.get(i)).as("ret[%d]", i).isEqualTo(expected.get(i));
}
soft.assertAll();
- describedAs(): as()메소드 대신 사용할 수 있다.
*전체 메소드는 공식문서 확인
https://assertj.github.io/doc/
참고: 최범균의 테스트 주도개발 시작하기
'Spring > Test' 카테고리의 다른 글
[Springboot] MockMvc를 이용해서 테스트하기 (1) | 2024.02.16 |
---|---|
[Springboot] SpringBoot 테스트 @SpringBootTest (0) | 2022.12.19 |
TDD, 대역을 이용한 테스트 (0) | 2022.11.30 |
TDD, 테스트 코드의 구성 (0) | 2022.11.30 |
TDD 테스트 주도 개발, 기능 명세와 설계 (0) | 2022.11.27 |