목차
isEqualTo()/isNotEqualTo
isNull() / isNotNull()
isSameAs() / isNotSameAs()
assertThatThrownBy().isInstanceOf()
assertThatCode().doesNotThrownAnyException()
asserThat -String관련
asserThat - Collection관련
Satisfy
Match
singleelement()
Filtering
extracting
exception Handling
JUnit 테스트코드를 먼저 학습하기를 권장합니다.
https://hellobrocolli.tistory.com/137
AssertJ란?
: 테스트 코드를 더 편리하게 작성하게 도와주는 라이브러리
: JUnit과 함께 사용가능
isEqualTo()
: 두 값이 같은지 비교
: assertEquals(expected, actual)과 비슷한 기능
@Test
@DisplayName("isEqualTo 메서드로 두 값이 같은지 비교한다")
void isEqualTo_메서드로_두_값이_같은지_비교한다() {
final var a = 1;
final var b = 2;
final var actual = a + b;
final var expected = 3;
// TODO: JUnit5의 assertEquals 메서드를 AssertJ의 isEqualTo 메서드로 변경해보세요.
assertThat(expected).isEqualTo(actual);
}
isNotEqualTo()
: 두 객체의 값이 다른지 비교할 때 사용
: assertNotEquals() 메서드와 비슷한 기능
@Test
@DisplayName("isNotEqualTo 메서드로 두 값이 다른지 비교한다")
void isNotEqualTo_메서드로_두_값이_다른지_비교한다() {
final var a = 1;
final var b = 2;
final var actual = a + b;
final var expected = 4;
// TODO: JUnit5의 assertNotEquals 메서드를 AssertJ의 isNotEqualTo 메서드로 변경해보세요.
assertThat(expected).isNotEqualTo(actual);
}
isNULL()
: 객체가 Null인지 비교
JUnit : assertNull()
Test
@DisplayName("isNull 메서드로 객체가 null인지 비교한다")
void isNull_메서드로_객체가_null인지_비교한다() {
final Object actual = null;
// TODO: JUnit5의 assertNull 메서드를 AssertJ의 isNull 메서드로 변경해보세요.
assertThat(actual).isNull();
}
isNotNull()
: 객체가 null이 아닌지 확인
JUnit : assertNotNull()
@Test
@DisplayName("isNotNull 메서드로 객체가 null이 아닌지 비교한다")
void isNotNull_메서드로_객체가_null이_아닌지_비교한다() {
final Object actual = new Object();
// TODO: JUnit5의 assertNotNull 메서드를 AssertJ의 isNotNull 메서드로 변경해보세요.
//assertNotNull(actual);
assertThat(actual).isNotNull();
}
isSameAs()
: 두 객체가 같은 객체인지 비교
JUnit: assertSame()
@Test
@DisplayName("isSameAs 메서드로 두 객체가 같은 객체인지 비교한다")
void isSameAs_메서드로_두_객체가_같은_객체인지_비교한다() {
final var actual = new Object();
final var expected = actual;
// TODO: JUnit5의 assertSame 메서드를 AssertJ의 isSameAs 메서드로 변경해보세요.
assertSame(actual, expected);
assertThat(actual).isSameAs(expected);
}
isNotSameAs()
: 두 객체가 같지 않은 객체인지 비교
JUnit: assertNotSame()
@Test
@DisplayName("isNotSameAs 메서드로 두 객체가 같은 객체인지 비교한다")
void isNotSameAs_메서드로_두_객체가_같은_객체인지_비교한다() {
final var actual = new Object();
final var expected = new Object();
// TODO: JUnit5의 assertNotSame 메서드를 AssertJ의 isNotSameAs 메서드로 변경해보세요.
assertNotSame(actual, expected);
assertThat(actual).isNotSameAs(expected);
}
assertThatThrownBy(ThrowingCallable).isInstanceOf()
: 특정 예외가 발생했는지 비교
JUnit : assertThrows()
@Test
@DisplayName("assertThatThrownBy 메서드로 특정 예외가 발생하는지 비교한다")
void assertThatThrownBy_메서드로_특정_예외가_발생하는지_비교한다() {
// TODO: JUnit5의 assertThrows 메서드를 AssertJ의 assertThatThrownBy 메서드로 변경해보세요.
assertThrows(IllegalCallerException.class, () -> {
causeException();
});
Assertions.assertThatThrownBy(()-> {
causeException();
}).isInstanceOf(IllegalCallerException.class);
}
=> assertThrows는 예외 타입만 비교가 가능
=> assertThatThrownBy는 예외 메시지도 비교 가능
Assertions.assertThatThrownBy(this ::causeException)
.isInstanceOf(IllegalCallerException.class)
.hasMessage("예외가 발생했습니다.");
assertThatCode(Executable) . doesNotThrowAnyException()
: 특정 코드가 예외를 발생하지 않는지
Junit : assertDoesNotThrow
@Test
@DisplayName("assertThatCode 메서드로 특정 코드가 예외를 발생하지 않는지 비교한다")
void assertThatCode_메서드로_특정_코드가_예외를_발생하지_않는지_비교한다() {
// TODO: JUnit5의 assertDoesNotThrow 메서드를 AssertJ의 assertThatCode 메서드로 변경해보세요.
assertDoesNotThrow(() -> {
final var number = Integer.valueOf(0x80000000);
});
assertThatCode(()-> {
final var number = Integer.valueOf(0x80000000);
}).doesNotThrowAnyException();
}
assertThat()
: AssertJ는 매개변수로 넘기는 타입에 맞게 메서드를 사용가능
>> String 관련 메서드
-.contains(expected)
: actual에 expected가 들어있는지 비교
@Test
@DisplayName("contains 메서드로 문자열에 특정 문자열이 포함되어 있는지 비교한다")
void contains_메서드로_문자열에_특정_문자열이_포함되어_있는지_비교한다() {
final var actual = "Hello, world!";
final var expected = "world";
// TODO: AssertJ의 contains 메서드를 사용하여 actual에 expected가 포함되어 있는지 비교해보세요.
assertTrue(actual.contains(expected));
assertThat(actual).contains(expected);
}
-.startsWith(expected)
-.endsWith(expected)
: 특정 문자열로 시작/끝나는지 비교
@Test
@DisplayName("startsWith 메서드로 문자열이 특정 문자열로 시작하는지 비교한다")
void startsWith_메서드로_문자열이_특정_문자열로_시작하는지_비교한다() {
final var actual = "Hello, world!";
final var expected = "Hello";
// TODO: AssertJ의 startsWith 메서드를 사용하여 actual이 expected로 시작하는지 비교해보세요.
assertTrue(actual.startsWith(expected));
assertThat(actual).startsWith(expected);
}
@Test
@DisplayName("문자열이 특정 문자열로 끝나는지 비교한다")
void 문자열이_특정_문자열로_끝나는지_비교한다() {
final var actual = "Hello, world!";
final var expected = "world!";
// TODO: AssertJ의 기능을 활용하여 문자열이 특정 문자열로 끝나는지 비교해보세요.
assertThat(actual).endsWith(expected);
}
-.matches(String regex)
: 정규 표현식과 일치하는지 비교
@Test
@DisplayName("문자열이 정규 표현식과 일치하는지 비교한다")
void matches_메서드로_문자열이_정규_표현식과_일치하는지_비교한다() {
final var actual = "Hello, world!";
final var expected = "Hello, [a-z]+!";
// TODO: AssertJ의 기능을 활용하여 문자열이 정규 표현식과 일치하는지 검증해보세요.
assertThat(actual).matches(expected);
}
>>Collection 관련 메서드 (assertThat(Collection c))
- .hasSize(int expected)
: 컬렉션의 사이즈를 체크
@Test
@DisplayName("Collection의 크기를 비교한다")
void Collection의_크기를_비교한다() {
final var actual = List.of(1, 2, 3);
final var expected = 3;
// TODO: AssertJ의 기능을 활용하여 Collection의 크기를 비교해보세요.
assertEquals(actual.size(), expected);
assertThat(actual).hasSize(expected);
}
- .contains(Object o)
: Collection에 특정 객체가 포함되어 있는지 비교
@Test
@DisplayName("Collection에 특정 객체가 포함되어 있는지 비교한다")
void Collection에_특정_객체가_포함되어_있는지_비교한다() {
final var actual = List.of(1, 2, 3);
final var expected = 1;
// TODO: AssertJ의 기능을 활용하여 Collection에 특정 객체가 포함되어 있는지 비교해보세요.
assertTrue(actual.contains(expected));
assertThat(actual).contains(expected);
}
- contains와 관한 다양한 메서드들
List<String> list = List.of("1", "1", "2", "3");
// 포함되어 있니
assertThat(list).contains("1", "2");
// 중복된 값도 반영됨
assertThat(list).containsOnly("2","1","3");
// 순서 까지 정확해야함
assertThat(list).containsExactly("1", "1", "2", "3");
// 순서 정확하지 않아도됨
assertThat(list).containsExactlyInAnyOrder("2", "3", "1", "1");
assertThat(list).contains("1").contains("1").containsSequence("2", "3");
// 오직 한번만 있는 값들
assertThat(list).containsOnlyOnce("2", "3");
assertThat(list).containsAnyOf("2");
-. extracting(필드이름)
: Collection에 포함된 객체들 중 특정 필드를 추출할 때 사용
- getter가 없이도 Reflection을 사용하여 추출가능
@Test
@DisplayName("extracting 메서드로 Collection에 포함된 객체들 중 특정 필드를 추출한다")
void extracting_메서드로_Collection에_포함된_객체들_중_특정_필드를_추출한다() {
class User {
private final String username;
private final String password;
User(final String username, final String password) {
this.username = username;
this.password = password;
}
public String getUsername() {
return username;
}
}
final var actual = List.of(
new User("user1", "password1"),
new User("user2", "password2"),
new User("user3", "password3")
);
final var expected = List.of("user1", "user2", "user3");
// TODO: AssertJ의 extracting 메서드를 사용하여 actual에 포함된 User 객체들 중 username 필드를 추출하여 expected와 비교해보세요.
for (int i = 0, end = actual.size(); i < end; i++) {
assertEquals(actual.get(i).getUsername(), expected.get(i));
}
assertThat(actual)
.extracting("username")
.containsExactlyElementsOf(expected);
}
추가
추가적으로 assertj 공식문서에서 괜찮은 기능 몇가지를 정리해보았다.
먼저 예제 클래스는 name과 age를 가지는 Hobbit 클래스이다.
public class Hobbit {
public String name;
public String age;
@Override
public String toString() {
return format("Hobbit [name=%s, age=%s]", name, age);
}
}
Satisfy
allSatisfy : 모두 만족하는지 확인
anySatisfy : 하나라도 조건을 만족하는지
noneSatisfy : 만족하는 것이 하나도 없는지
public SELF allSatisfy(Consumer<? super ELEMENT> requirements)
=> 매개변수는 있지만 반환값은 없는 람다식 사용 -> ThrowingConsumer를 통해 오류발생 여부 탐색
List<TolkienCharacter> hobbits = list(frodo, sam, pippin);
// allSatisfy : 조건을 모두 만족하는지 확인한다
// 조건1. 모든 hobbit들의 종족이 HOBBIT
// 조건2. 이름은 사우론이 아니어야 한다.
assertThat(hobbits).allSatisfy(character -> {
assertThat(character.getRace()).isEqualTo(HOBBIT);
assertThat(character.getName()).isNotEqualTo("Sauron");
});
//anySatisfy: 하나라도 만족하는지 확인한다
// 조건1. 종족이 HOBBIT
// 조건2. 이름이 Sam
assertThat(hobbits).anySatisfy(character -> {
assertThat(character.getRace()).isEqualTo(HOBBIT);
assertThat(character.getName()).isEqualTo("Sam");
});
//noneSatisfy : 만족하는 것이 하나도 없는지 확인한다.
// 조건1. 종족이 ELF
assertThat(hobbits).noneSatisfy(character ->
assertThat(character.getRace()).isEqualTo(ELF));
# 만약 allSatisfy가 실패한다면, 실패한 assertion이 출력된다.
Match
allMatch : 조건을 모두 만족하는지 확인한다.
anyMatch : 조건을 하나라도 만족하는지 확인한다.
noneMatch : 조건을 모두 만족하지 않는지 확인한다.
List<TolkienCharacter> hobbits = list(frodo, sam, pippin);
//allMatch : 모두 만족해야 할 사안
//anyMatch : 하나라도 만족해야 할 사안
//noneMatch : 하나도 만족하지 못해야 하는 사안
assertThat(hobbits).allMatch(character -> character.getRace() == HOBBIT, "hobbits") // 뒤에 있는 hobbits는 PredicateDescription
.anyMatch(character -> character.getName().contains("pp"))
.noneMatch(character -> character.getRace() == ORC);
singleElement() : 오직 하나의 element를 가지는지 확인
Iterable<String> babySimpsons = list("Maggie");
// only object assertions available
assertThat(babySimpsons).singleElement()
.isEqualTo("Maggie");
Filtering
- filteredOn(조건식)
- private field를 읽는 것은 default로 가능하다.
- 그러나 불가능하게 만드는 설정도 가능하다
Assertions.setAllowExtractingPrivateFields(false)
filter는 기본적으로 조건에 부합하는 요소들을 걸러준다.
assertThat(fellowshipOfTheRing).filteredOn( character -> character.getName().contains("o") )
.containsOnly(aragorn, frodo, legolas, boromir);
property를 비교할 때 not, in notin등을 사용할 수 있다.
import static org.assertj.core.api.Assertions.in;
import static org.assertj.core.api.Assertions.not;
import static org.assertj.core.api.Assertions.notIn;
...
// filters use introspection to get property/field values
assertThat(fellowshipOfTheRing).filteredOn("race", HOBBIT)
.containsOnly(sam, frodo, pippin, merry);
// 내부 클래스 속성도 지원한다
// race.name이 Man인 것들을 필터링
assertThat(fellowshipOfTheRing).filteredOn("race.name", "Man")
.containsOnly(aragorn, boromir);
// race가 [HOBBIT, MAN]에 속하지 않는 것
assertThat(fellowshipOfTheRing).filteredOn("race", notIn(HOBBIT, MAN))
.containsOnly(gandalf, gimli, legolas);
// race가 [MAIA, MAN]에 속하는 것
assertThat(fellowshipOfTheRing).filteredOn("race", in(MAIA, MAN))
.containsOnly(gandalf, boromir, aragorn);
// race가 HOBBIT이 아닌 것
assertThat(fellowshipOfTheRing).filteredOn("race", not(HOBBIT))
.containsOnly(gandalf, boromir, aragorn, gimli, legolas);
//여러가지 기준을 Chaining 할 수 있다
assertThat(fellowshipOfTheRing).filteredOn("race", MAN)
.filteredOn("name", not("Boromir"))
.containsOnly(aragorn);
filteredOnNull : 지정된 property가 null인 객체를 반환한다.
TolkienCharacter pippin = new TolkienCharacter("Pippin", 28, HOBBIT);
TolkienCharacter frodo = new TolkienCharacter("Frodo", 33, HOBBIT);
TolkienCharacter merry = new TolkienCharacter("Merry", 36, HOBBIT);
TolkienCharacter mysteriousHobbit = new TolkienCharacter(null, 38, HOBBIT);
List<TolkienCharacter> hobbits = list(frodo, mysteriousHobbit, merry, pippin);
assertThat(hobbits).filteredOnNull("name")) //name이 null인 mysteriousHobbit 반환
.singleElement()
.isEqualTo(mysteriousHobbit);
Condition이라는 확장된 AssertJ 기능을 활용하여 filteredOn 안에 넣어두어도 된다.
여러 테스트가 공통의 Condition을 공유할 때 좋다
import org.assertj.core.api.Condition;
Condition<Player> mvpStats= new Condition<Player>(player -> {
return player.pointsPerGame() > 20 && (player.assistsPerGame() >= 8 || player.reboundsPerGame() >= 8);
}, "mvp");
List<Player> players;
players.add(rose); // Derrick Rose: 25 ppg - 8 assists - 5 rebounds
players.add(lebron); // Lebron James: 27 ppg - 6 assists - 9 rebounds
players.add(noah); // Joachim Noah: 8 ppg - 5 assists - 11 rebounds
// noah does not have more than 20 ppg
assertThat(players).filteredOn(mvpStats)
.containsOnly(rose, lebron);
extracting
-만약 TolkeinCharacter List에서 name 필드를 검증하는 경우를 살펴보자
- stream을 통해 하나하나 getter를 하는 과정은 tedious하다
- extracting은 속성을 추출하는 과정을 편하게 바꿀 수 있다
// "name" needs to be either a property or a field of the TolkienCharacter class
assertThat(fellowshipOfTheRing).extracting("name")
.contains("Boromir", "Gandalf", "Frodo", "Legolas")
.doesNotContain("Sauron", "Elrond");
// specifying nested field/property is supported
assertThat(fellowshipOfTheRing).extracting("race.name")
.contains("Man", "Maia", "Hobbit", "Elf");
// same thing with a lambda which is type safe and refactoring friendly:
assertThat(fellowshipOfTheRing).extracting(TolkienCharacter::getName)
.contains("Boromir", "Gandalf", "Frodo", "Legolas");
// same thing map an alias of extracting:
assertThat(fellowshipOfTheRing).map(TolkienCharacter::getName)
.contains("Boromir", "Gandalf", "Frodo", "Legolas")
extracting에 두번째 매개변수로 type을 주어 strongly typed하게 만들 수 있다.
// String.class를 주어 강하게 타입화
assertThat(fellowshipOfTheRing).extracting("name", String.class)
.contains("Boromir", "Gandalf", "Frodo", "Legolas")
.doesNotContain("Sauron", "Elrond");
extracting으로 여러값을 추출하면 tuple로 반환된다
// when checking several properties/fields you have to use tuples:
import static org.assertj.core.api.Assertions.tuple;
// extracting name, age and race.name nested property
assertThat(fellowshipOfTheRing).extracting("name", "age", "race.name") //여러값 추출
.contains(tuple("Boromir", 37, "Man"), // tuple로 반환됨
tuple("Sam", 38, "Hobbit"),
tuple("Legolas", 1000, "Elf"));
// same assertion with functions for type safety:
assertThat(fellowshipOfTheRing).extracting(TolkienCharacter::getName,
tolkienCharacter -> tolkienCharacter.age,
tolkienCharacter -> tolkienCharacter.getRace().getName())
.contains(tuple("Boromir", 37, "Man"),
tuple("Sam", 38, "Hobbit"),
tuple("Legolas", 1000, "Elf"));
flatExtracting을 통해 flatMap 처럼 객체 깊이 있는 속성들을 일자화할 수 있다
Player jordan = ... // initialized with Pippen and Kukoc team mates
Player magic = ... // initialized with Jabbar and Worthy team mates
List<Player> reallyGoodPlayers = list(jordan, magic);
// check all team mates by specifying the teamMates property (Player has a getTeamMates() method):
assertThat(reallyGoodPlayers).flatExtracting("teamMates")
.contains(pippen, kukoc, jabbar, worthy);
// alternatively, you can use a Function for type safety:
assertThat(reallyGoodPlayers).flatExtracting(BasketBallPlayer::getTeamMates)
.contains(pippen, kukoc, jabbar, worthy);
// flatMap is an alias of flatExtracting:
assertThat(reallyGoodPlayers).flatMap(BasketBallPlayer::getTeamMates)
.contains(pippen, kukoc, jabbar, worthy);
// if you use extracting instead of flatExtracting the result would be a list of list of players so the assertion becomes:
assertThat(reallyGoodPlayers).extracting("teamMates")
.contains(list(pippen, kukoc), list(jabbar, worthy));
예외 처리
정해진 Exception을 잡는 테스트
- catchException
- catchIllegalArgumentException
- catchIllegalStateException
- catchIndexOutOfBoundsException
- catchIOException
- catchNullPointerException
- catchReflectiveOperationException
- catchRuntimeException
assertThatThrownBy(ThrowingCallable) : 예외 발생
assertThatThrownBy(() -> { throw new Exception("boom!"); }).isInstanceOf(Exception.class)
.hasMessageContaining("boom");
assertThatExceptionOfType(Exception.class).isThrownBy(ThrowingCallable)
assertThatExceptionOfType(IOException.class).isThrownBy(() -> { throw new IOException("boom!"); })
.withMessage("%s!", "boom")
.withMessageContaining("boom")
.withNoCause();
더 자연스러운 문법들도 있다.
- assertThatNullPointerException
- assertThatIllegalArgumentException
- assertThatIllegalStateException
- assertThatIOException
assertThatIOException().isThrownBy(() -> { throw new IOException("boom!"); })
.withMessage("%s!", "boom")
.withMessageContaining("boom")
.withNoCause();
reference)
https://umanking.github.io/2021/06/26/assertj-iteration/
https://assertj.github.io/doc/
'우테코 > Level1' 카테고리의 다른 글
[우테코-Lv1] 자동차 경주 간단한 회고 (계속 수정 예정) (0) | 2024.02.22 |
---|---|
List.of() vs Arrays.asList() 차이 / 갱신 가능 여부 (0) | 2024.02.22 |
[Intellij IDEA] 페어 프로그래밍 공유 환경설정 / 같은 IDE 공유하기 (0) | 2024.02.19 |
[스크랩] git 커밋 메시지 타입 (0) | 2024.02.16 |
POSIX new line : 파일 끝에 개행을 추가해야 하는 이유 (1) | 2024.02.16 |