
AssertJ Core란?
AssertJ is a Java library that provides a rich set of assertions and
truly helpful error messages, improves test code readability,
and is designed to be super easy to use within your favorite IDE.
AssertJ는 Java 라이브러리로, 다양한 조건과 유용한 에러 메시지를 제공하고
테스트 코드의 가독성을 높이며 여러분이 좋아하는 IDE 내에서 매우 쉽게 사용하도록 설계되었습니다.
http://www.javadoc.io/doc/org.assertj/assertj-core/ is the latest version of assertj core javadoc,
each assertion is explained, most of them with code examples so be sure to check it
if you want to know what a specific assertion does.
http://www.javadoc.io/doc/org.assertj/assertj-core/는 assertJ core에 대한 최신 Java 문서입니다.
각각의 assertion에 대해 설명되어 있으며 대부분 코드 예제가 포함되어 있습니다.
특정 assertion이 무엇을 하는지 알고 싶은 경우 참고하세요.
Here are a few examples of AssertJ assertions:
AssertJ assertions에 대한 몇 가지 예시:
// entry point for all assertThat methods and utility methods (e.g. entry)
// 모든 assertThat 메소드 및 유틸리티 메소드를 사용하기 위해 import합니다.
import static org.assertj.core.api.Assertions.*;
// basic assertions
// 일치하는 경우와 일치하지 않는 경우를 검사합니다.
assertThat(frodo.getName()).isEqualTo("Frodo");
assertThat(frodo).isNotEqualTo(sauron);
// chaining string specific assertions
// chaining을 사용하여 조금 더 구체적으로 검사할 수 있습니다.
assertThat(frodo.getName()).startsWith("Fro") // 시작 조건
.endsWith("do") // 끝 조건
.isEqualToIgnoringCase("frodo"); // 대소문자 구분 없이 일치하는지 검사
// collection specific assertions (there are plenty more)
// in the examples below fellowshipOfTheRing is a List<TolkienCharacter>
assertThat(fellowshipOfTheRing).hasSize(9)
.contains(frodo, sam)
.doesNotContain(sauron);
// as() is used to describe the test and will be shown before the error message
// as()는 테스트를 설명하는 데 사용되며 오류 메시지 앞에 표시됩니다.
assertThat(frodo.getAge()).as("check %s's age", frodo.getName()).isEqualTo(33);
// exception assertion, standard style ...
// 예외 검사의 표준 사용 방법입니다.
assertThatThrownBy(() -> { throw new Exception("boom!"); }).hasMessage("boom!");
// ... or BDD style
Throwable thrown = catchThrowable(() -> { throw new Exception("boom!"); });
assertThat(thrown).hasMessageContaining("boom");
// using the 'extracting' feature to check fellowshipOfTheRing character's names
assertThat(fellowshipOfTheRing).extracting(TolkienCharacter::getName)
.doesNotContain("Sauron", "Elrond");
// extracting multiple values at once grouped in tuples
// 튜플로 그룹화된 여러 값을 한 번에 추출합니다.
assertThat(fellowshipOfTheRing).extracting("name", "age", "race.name")
.contains(tuple("Boromir", 37, "Man"),
tuple("Sam", 38, "Hobbit"),
tuple("Legolas", 1000, "Elf"));
// filtering a collection before asserting
// 검사 전에 "o"를 포함한 이름들만 필터링합니다.
assertThat(fellowshipOfTheRing).filteredOn(character -> character.getName().contains("o"))
.containsOnly(aragorn, frodo, legolas, boromir);
// combining filtering and extraction (yes we can)
// 필터링과 추출을 같이 사용할 수도 있습니다.
assertThat(fellowshipOfTheRing).filteredOn(character -> character.getName().contains("o"))
.containsOnly(aragorn, frodo, legolas, boromir)
.extracting(character -> character.getRace().getName())
.contains("Hobbit", "Elf", "Man");
// and many more assertions: iterable, stream, array, map, dates, path, file, numbers, predicate, optional ...
Let’s start with a simple example showing a few important things.
몇 가지 중요한 것들을 보여주는 간단한 예부터 시작하겠습니다.
import static org.assertj.core.api.Assertions.assertThat; // 1. import
import org.junit.jupiter.api.Test;
public class SimpleAssertionsExample {
@Test
void a_few_simple_assertions() {
assertThat("The Lord of the Rings") // 2. 테스트 대상이 되는 하나의 객체를 파라미터로 전달
.isNotNull() // 3. 코드 자동 완성 기능을 사용하여 탐색한 뒤 원하는 assertion을 호출
.startsWith("The") // 4. 계속해서 필요한 assertions 체이닝 가능
.contains("Lord")
.endsWith("Rings");
}
}
Except for isNotNull which is a base assertion,
the other assertions are String specific as our object under test is a String.
기본 assertion인
isNotNull을 제외한 나머지 assertions은String에 특정된 assertion입니다.
It is often valuable to describe the assertion performed,
especially for boolean assertions where the default error message just complains that it got false instead of true (or vice versa).
기본 오류 메시지가 참 또는 거짓을 반환하는 boolean assertion인 경우, 어떤 assertion을 수행했는지 설명하는 것이 좋습니다.
You can set such a description with as(String description, Object... args)
but remember to do it before calling the assertion otherwise it is simply ignored as a failing assertion breaks the chained calls.
as(String description, Obje... args)로 설명을 설정할 수 있습니다. 단, assertion을 호출하기 전에 먼저 설정해야 합니다. 그렇지 않으면 연결된 그 뒤로 연결된 호출을 무시하며 중단됩니다.
Example of a failing assertion with a description:
실패한 예시:
TolkienCharacter frodo = new TolkienCharacter("Frodo", 33, Race.HOBBIT);
// failing assertion, remember to call as() before the assertion!
// 반드시 assertion 사용 전에 as()를 호출해야 합니다!
assertThat(frodo.getAge()).as("check %s's age", frodo.getName())
.isEqualTo(100);
The error message starts with the given description in [] :
에러 메시지는
[]안의 설명으로 시작합니다 :
[check Frodo's age] expected:<100> but was:<33>
// 예상 결과 : <100> 수행 결과 : <33>
AssertJ can print each assertion description (when it is set), to do so call Assertions.setPrintAssertionsDescription(true);.
If printing assertion descriptions is not what you need, you can alternatively register a Consumer<Description> that will be called each time a description is set.
Both options are exposed in AssertJ Configuration class.
AssertJ는
Assertions.setPrintAssertionsDescription(true);을 호출하여 설명을 출력할 수 있습니다.
만약 출력 형식이 당신이 필요로 하는 것과 다르다면,Consumer<Description>을 사용하여 직접 설명 형식을 설정할 수 있습니다.
두 가지 옵션 모두 AssertJ의Configuration클래스와 연관됩니다.
Example: using a description consumer
// initialize the description consumer
final StringBuilder descriptionReportBuilder = new StringBuilder(String.format("Assertions:%n")); // %n : 줄 바꾸기
Consumer<Description> descriptionConsumer = desc -> descriptionReportBuilder.append(String.format("-- %s%n", desc)); // %s: desc, %n: 줄 바꾸기
// use the description consumer for any following assertions descriptions.
// 이후 assertions에 대한 설명 설정 시, 위의 consumer를 사용하도록 설정
Assertions.setDescriptionConsumer(descriptionConsumer);
// execute some assertions
TolkienCharacter frodo = new TolkienCharacter("Frodo", 33, Race.HOBBIT);
assertThat(frodo.getName()).as("check name") // 설명 : "check name"
.isEqualTo("Frodo");
assertThat(frodo.getAge()).as("check age") // 설명 : "check age"
.isEqualTo(33);
// get the report
String descriptionReport = descriptionReportBuilder.toString(); // builder에 저장된 수행 log를 받아옵니다.
resulting descriptionReport:
Assertions:
-- check name
-- check age
AssertJ tries its best to give helpful error messages,
but you can always change it with overridingErrorMessage() or withFailMessage().
AssertJ는 유용한 에러 메시지를 제공하기 위해 최선을 다하지만,
언제든지overridingErrorMessage()또는withFailMessage()로 에러 메시지를 변경할 수 있습니다.
Example with this failing test:
TolkienCharacter frodo = new TolkienCharacter("Frodo", 33, Race.HOBBIT);
TolkienCharacter sam = new TolkienCharacter("Sam", 38, Race.HOBBIT);
// failing assertion, remember to call withFailMessage/overridingErrorMessage before the assertion!
// 항상 assertion 전에 호출해야 한다는 것을 잊지마세요!
assertThat(frodo.getAge()).withFailMessage("should be %s", frodo) // 실패할 경우 출력할 메시지 재정의
.isEqualTo(sam);
The error message is:
실패한 테스트의 에러 메시지:
java.lang.AssertionError: should be TolkienCharacter [name=Frodo, age=33, race=HOBBIT]
지연된 오류 메시지 재정의
If the error message is expensive to build,
use the overloaded methods taking a Supplier<String> instead of a String,
the message will only be built if the assertion fails.
오류 메시지를 작성하는 데 비용이 많이 드는 경우에는
String대신Supplier<String>을 사용하는 메서드를 사용합니다.
그러면 assertion이 실패하는 경우에만 에러 메시지를 생성합니다.
Example:
// 연산이 수행될 때 처리하는 stream과 유사
assertThat(player.isRookie()).overridingErrorMessage(() -> "Expecting Player to be a rookie but was not.")
.isTrue();
assertThat(player.isRookie()).withFailMessage(() -> "Expecting Player to be a rookie but was not.")
.isTrue();
잘못된 사용을 피하기
There are a few things to keep in mind when using AssertJ to avoid misusing it.
AssertJ를 잘못 사용하지 않도록 염두에 두어야 할 몇 가지 사항이 있습니다.
assertion을 호출하는 것을 잊는 것
The main trap is to pass the object under to test to assertThat()
and forget to call an assertion afterward. This misuse can be detected by
SpotBugs or Findbugs thanks to the @CheckReturnValue annotation on all assertThat() methods.
주된 실수 중 하나는 테스트할 객체를
assertThat()에 전달하고 나중에 assertion을 호출하지 않는 것입니다.
Here’s what it looks like in SpotBugs:

Figure 1. SpotBugs detecting AssertJ invalid usage
The following examples show incorrect AssertJ API usage to avoid!
다음 예는 피해야 할 잘못된 AssertJ API 사용입니다!
Bad
// DON'T DO THIS ! It does not assert anything
// 이렇게 하지마세요! 아무런 assertion이 없습니다.
assertThat(actual.equals(expected)); // 객체를 담아야하는 assertThat에 assertion까지 작성한 오류
Good
// DO THIS:
// 이렇게 하세요:
assertThat(actual).isEqualTo(expected); // assertThat에 객체를 담고 이어서 assertion 호출
// OR THIS (less classy but ok):
// 이렇게도 가능합니다 (고급스럽지는 않지만 나쁘지 않습니다):
assertThat(actual.equals(expected)).isTrue(); // 'boolean 값이 true이다'라는 assertion 작성
Bad
// DON'T DO THIS ! It does not assert anything and passes
// 이렇게 하지마세요! 아무것도 주장하지 않고 통과합니다.
assertThat(1 == 2);
Good
// DO THIS: (fails as expected)
// 이렇게 하세요: (실패가 예상됩니다)
assertThat(1).isEqualTo(2);
// OR THIS (less classy but ok):
// 이렇게도 가능합니다 (고급스럽지는 않지만 나쁘지 않습니다):
assertThat(1 == 2).isTrue();
assertion 후에 as()를 호출하는 것
Describing an assertion must be done before calling the assertion
otherwise it is ignored as a failing assertion breaks will prevent the call to as().
assertion을 호출하기 전에 assertion을 설명해야 합니다. 그렇지 않으면 assertion 중단이 실패하면서
as()에 대한 호출이 차단되므로 무시됩니다.
Bad
// DON'T DO THIS ! as/describedAs have no effect after the assertion
// 이렇게 하지마세요! as 또는 describedAs는 assertion 뒤에서 어떠한 작동도 하지 않습니다.
assertThat(actual).isEqualTo(expected).as("description");
assertThat(actual).isEqualTo(expected).describedAs("description");
Good
// DO THIS: use as/describedAs before the assertion
// 이렇게 하세요: assertion 호출 전에 as 또는 describedAs를 사용하세요.
assertThat(actual).as("description").isEqualTo(expected);
assertThat(actual).describedAs("description").isEqualTo(expected);
assertion 후에 withFailMessage 또는 overridingErrorMessage를 호출하는 것
Setting an error message must be done before calling the assertion
otherwise it is ignored as a failing assertion breaks will prevent
the call to withFailMessage() / overridingErrorMessage().
에러 메시지는 반드시 assertion을 호출하기 전에 설정해야 합니다.
그렇지 않으면 assertion 중단 실패로 인해usingComparator()또는usingElementComparator()호출이 차단되므로 무시됩니다.
Bad
// DON'T DO THIS ! overridingErrorMessage/withFailMessage have no effect after the assertion
// 이렇게 하지 마세요! assertion을 호출한 뒤에 사용한 overridingErrorMessage 또는 withFailMessage는 작동하지 않습니다.
assertThat(actual).isEqualTo(expected).overridingErrorMessage("custom error message");
assertThat(actual).isEqualTo(expected).withFailMessage("custom error message");
Good
// DO THIS: use overridingErrorMessage/withFailMessage before the assertion
// 이렇게 하세요: assertion을 호출하기 전에 overridingErrorMessage 또는 withFailMessage를 사용하세요.
assertThat(actual).overridingErrorMessage("custom error message").isEqualTo(expected);
assertThat(actual).withFailMessage("custom error message").isEqualTo(expected);
assertion 선언 후 comparator를 설정하는 것
Setting comparators must be done before calling the assertion
otherwise it is ignored as a failing assertion breaks will prevent
the call to usingComparator() / usingElementComparator().
comparator는 반드시 assertion을 호출하기 전에 설정해야 합니다.
그렇지 않으면 assertion 중단 실패로 인해usingComparator()또는usingElementComparator()호출이 차단되므로 무시됩니다.
Bad
// DON'T DO THIS ! Comparator is not used
// 이렇게 하지 마세요! Comparator를 사용하지 않습니다.
assertThat(actual).isEqualTo(expected).usingComparator(new CustomComparator());
Good
// DO THIS:
// 이렇게 하세요:
assertThat(actual).usingComparator(new CustomComparator()).isEqualTo("a"); // 항상 순서를 신경써서 사용
AssertJ 구성 설정하기
This section describes the different ways to configure AssertJ,
either by setting configuration properties individually or globally using the Configuration class.
이번에는 속성을 개별적으로 설정하거나,
Configuration클래스를 통해 전역적으로 설정하는 등 AssertJ를 구성하는 다양한 방법에 대해 설명합니다.
To be effective the configuration changes must be applied before the tests are executed,
depending on the scope of the tests this means different things:
효과적으로 설정을 변경하기 위해서는 반드시 테스트가 실행되기 전에 적용해야 합니다.
테스트 범위에 따라 의미가 달라질 수 있습니다:
@AfterEach method (JUnit 5).@BeforeAll method@AfterAll method (JUnit 5).BeforeAllCallback.Configuration subclass and let AssertJ discover it automagically.
- 단일 테스트의 경우: 테스트에서 설정을 변경하고,
@AfterEach메서드에서 되돌립니다.- 클래스 내 모든 테스트:
@BeforeAll메서드에서 구성을 변경하고@AfterAll메서드에서 변경 사항을 되돌립니다.- 테스트 전에 구성 및 설정을 변경하고 싶다면 다음과 같은 옵션을 설정할 수 있습니다:
BeforeAllCallback을 구현한 Unit 5 확장프로그램을 사용하세요.Configuration의 하위 클래스를 등록한 뒤, AssertJ가 자동으로 찾아서 적용하도록 합니다.
단일 속성 구성하기
The Assertions class provides static methods to change each configuration properties.
Assertions클래스는 각각의 구성 설정을 변경할 수 있는 static 메서드를 제공합니다.
Assertions.setAllowComparingPrivateFields(true);
Assertions.setAllowExtractingPrivateFields(false);
Assertions.setExtractBareNamePropertyMethods(false);
Assertions.setLenientDateParsing(true);
Assertions.setMaxElementsForPrinting(100);
Assertions.setMaxLengthForSingleLineDescription(250);
Assertions.setRemoveAssertJRelatedElementsFromStackTrace(true);
Assertions.useRepresentation(myRepresentation);
Assertions.registerCustomDateFormat(myCustomDateFormat);
Assertions.setPrintAssertionsDescription(true);
Assertions.setConsumerDescription(description -> writeToFile(description, report));
Assertions.useRepresentation(myRepresentation);
This property allows you to register a Representation to control the way AssertJ formats the different types
displayed in the assertion error messages. Consult the Controlling type formatting chapter for details.
이 속성을 사용하면, AssertJ의 오류 메시지에 표시되는 다양한 유형의 형식을 지정하는 방식을 제어하는
Representation을 등록할 수 있습니다.
자세한 내용은 Controlling type formatting 챕터를 참고하세요.
Defaults to StandardRepresentation.
기본값은
StandardRepresentation입니다.
Assertions.setAllowComparingPrivateFields(true);
Globally sets whether the use of private fields is allowed for field/property by field/property comparison. Defaults to true.
필드 또는 속성 비교를 위한 private 필드 사용 여부를 전역적으로 설정합니다.
기본값은 true입니다.
Assertions.setAllowExtractingPrivateFields(false);
Globally sets whether the AssertJ extracting capability should be allowed to extract private fields. Defaults to true.
private 필드를 추출 허용 여부를 전역적으로 설정합니다.
기본값은 true입니다.
Assertions.setExtractBareNamePropertyMethods(false);
Globally sets whether the AssertJ extracting capability considers bare-named property methods like String name(). Defaults to true.
AssertJ
extracting을 사용한 private 필드 추출 허용 여부를 전역적으로 설정합니다.
기본값은 true입니다.
extracting을 사용하면 객체의 속성 값을 한 번에 검증할 수 있다고 해요.Person person = new Person("John", 30); assertThat(person).extracting(Person::getName, Person::getAge).containsExactly("John", 30);
extracting에 여러 개의 람다식을 전달하면 추출된 값들이 Tuple 형태로 반환됩니다.
그리고containsExactly를 사용하여 name과 age 속성의 값이 순서에 따라 정확히 일치하는지 검증합니다.
containsExactlyInAnyOrder를 사용하면 순서와 상관없이 정확한 값들이 포함되어 있는지 검증할 수 있습니다.
Assertions.setLenientDateParsing(true);
Specify whether or not date/time parsing is to be lenient for AssertJ default date formats. With lenient parsing, the parser may use heuristics to interpret inputs that do not precisely match this object’s format. With strict parsing, inputs must match this object’s format.
AssertJ에서 사용하는 기본적인 날짜 형식에 대해서 파싱 시 유연하게 처리할 것인지, 엄격하게 처리할 것인지를 설정하는 옵션입니다.
유연한 구문 분석을 사용하면 파서는 휴리스틱을 사용하여 이 개체의 형식과 정확히 일치하지 않는 입력을 추측하여 해석할 수 있습니다.
엄격한 구문 분석을 사용하면 입력이 형식과 완전히 일치해야 합니다.
Assertions.registerCustomDateFormat(myCustomDateFormat);
In addition to the default date formats, you can register some custom ones
that AssertJ will use in date assertions (see also Assertions.registerCustomDateFormat).
Note that custom date formats take precedence over default ones.
기본 날짜 형식 외에도 AssertJ의 날짜 관련 assertion에서 사용할 사용자 지정 형식을 등록할 수 있습니다.
사용자 지정 날짜 형식은 기본 날짜 형식보다 우선됩니다.
Assertions.setMaxElementsForPrinting(100);
In error messages, sets the threshold for how many elements from one iterable/array/map will be included in the in the description.
Defaults to 1000.
The point of this property is to avoid printing iterable/array/map with too many elements in error messages.
오류 메시지에서 하나의 배열 또는 맵의 얼마나 많은 요소가 설명에 포함될지에 대한 임계값을 설정합니다.
기본값은 1000입니다.
이 속성의 포인트는 오류 메시지에 너무 많은 요소를 인쇄하지 않도록 하는 것입니다.
Assertions.setMaxLengthForSingleLineDescription(250);
In error messages, sets the threshold when iterable/array formatting will be on one line
(if their String description is less than this parameter) or it will be formatted with one element per line. Defaults to 80.
오류 메시지에서 한 줄에 표시되는 배열의 임계값을 설정합니다.
기본값은 80입니다.
Example:
String[] greatBooks = array("A Game of Thrones", "The Lord of the Rings", "Assassin's Apprentice");
this array is formatted on one line as its length < 80:
이 배열은 길이가 80보다 작으므로 한 줄에 모두 표시됩니다.
["A Game of Thrones", "The Lord of the Rings", "Assassin's Apprentice"]
Whereas this array ...
반면 이 배열은 ...
String[] greatBooks = array("A Game of Thrones", "The Lord of the Rings", "Assassin's Apprentice", "Guards! Guards! (Discworld)");
... is formatted on multiple lines with one element per line:
한 줄에 하나씩, 여러 줄로 형식이 지정됩니다.
["A Game of Thrones",
"The Lord of the Rings",
"Assassin's Apprentice",
"Guards! Guards! (Discworld)"]
Sets whether the elements related to AssertJ are removed from assertion errors stack trace. Defaults to true.
AssertJ와 관련된 오류를 stack trace에서 제거할지 설정합니다.
기본값은 true입니다.
Since 3.13.0, AssertJ exposes a org.assertj.core.configuration.Configuration object
providing access to all AssertJ globally configurable properties.
3.13.0부터 AssertJ는 전역적으로 구성 가능한 속성에 대한 접근을 제공하는
org.assertj.core.configuration.Configuration를 제공합니다.
You can create an instance of org.assertj.core.configuration.Configuration
and change individual properties through setters or create your own custom configuration
by inheriting from it and overriding the methods
to change the default behavior as in the CustomConfiguration example below.
여러분은
org.assertj.core.configuration.Configuration의 인스턴스를 생성할 수 있으며,
setter를 사용하여 각각의 속성을 변경할 수 있습니다.
또한 아래의 예시처럼 configuration을 상속받는 커스텀 설정 클래스를 만들고 메서드 오버라이딩을 통해 기본 동작을 변경할 수 있습니다.
Your configuration will be effective once you call
Configuration.apply() or Configuration.applyAndDisplay().
Configuration.apply()또는Configuration.applyAndDisplay()를 호출하면 구성 설정이 적용됩니다.
Example:
Configuration configuration = new Configuration();
configuration.setBareNamePropertyExtraction(false);
configuration.setComparingPrivateFields(false);
configuration.setExtractingPrivateFields(false);
configuration.setLenientDateParsing(true);
configuration.setMaxElementsForPrinting(1001);
configuration.setMaxLengthForSingleLineDescription(81);
configuration.setRemoveAssertJRelatedElementsFromStackTrace(false);
// don't forget to apply it!
// 설정을 적용하는 것을 잊지마세요!
configuration.applyAndDisplay();
Printing the above configuration produces the following output:
위의 설정을 출력하면 아래와 같은 결과가 나옵니다:
Applying configuration org.assertj.core.configuration.Configuration
- representation .................................. = BinaryRepresentation
- comparingPrivateFieldsEnabled ................... = false
- extractingPrivateFieldsEnabled .................. = true
- bareNamePropertyExtractionEnabled ............... = false
- lenientDateParsingEnabled ....................... = true
- additionnal date formats ........................ = [yyyy_MM_dd, yyyy|MM|dd]
- maxLengthForSingleLineDescription ............... = 150
- maxElementsForPrinting .......................... = 2000
- removeAssertJRelatedElementsFromStackTraceEnabled = true
자동화된 구성 검색
This section describes a way to register an AssertJ Configuration
without using any test framework hooks like BeforeAllCallback.
이 섹션에서는
BeforeAllCallback과 같은 테스트 프레임워크에서 제공하는 콜백 메서드를 사용하지 않고 AssertJ 구성을 등록하는 방법을 설명합니다.
Follow the steps below to register your Configuration as an SPI:
아래 단계에 따라 구성을 SPI로 등록하세요:
org.assertj.core.configuration.Configurationorg.assertj.core.configuration.Configuration in a META-INF/services directoryMETA-INF/services/ is in the runtime classpath, usually putting it in src/test/resources will do.Configuration in services/org.assertj.core.configuration.Configuration.
org.assertj.core.configuration.Configuration를 상속하는 구성을 만듭니다.META-INF/services에org.assertj.core.configuration.Configuration이라는 파일을 만듭니다.META-INF/services/가 런타임 클래스 경로에 있는지 확인하십시오. 일반적으로src/test/resources에 넣으면 됩니다.services/org.assertj.core.configuration.Configuration에 구성의 클래스 이름을 입력합니다.
This is all you have to do, AssertJ will pick up the Configuration automatically and display it at the first interaction with AssertJ.
이것이 여러분이 해야 할 전부입니다. AssertJ는 자동으로 구성을 선택하고 AssertJ와의 첫 번째 상호 작용에서 이를 표시합니다.
Here’s an example of a custom configuration class:
다음은 사용자 지정 구성 클래스의 예입니다:
package example.core;
import static org.assertj.core.presentation.BinaryRepresentation.BINARY_REPRESENTATION;
import static org.assertj.core.util.Lists.list;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.List;
import org.assertj.core.configuration.Configuration;
import org.assertj.core.presentation.Representation;
class CustomConfiguration extends Configuration { // Configuration을 상속
private static final SimpleDateFormat DATE_FORMAT1 = new SimpleDateFormat("yyyy_MM_dd");
private static final SimpleDateFormat DATE_FORMAT2 = new SimpleDateFormat("yyyy|MM|dd");
// we keep the default behavior for extractingPrivateFieldsEnabled since it is not overridden
// 재정의되지 않았기 때문에 extractingPrivateFieldsEnabled에 대한 기본 동작을 유지합니다.
@Override
public Representation representation() {
return BINARY_REPRESENTATION;
}
@Override
public boolean bareNamePropertyExtractionEnabled() {
return false;
}
@Override
public boolean comparingPrivateFieldsEnabled() {
return false;
}
@Override
public boolean lenientDateParsingEnabled() {
return true;
}
@Override
public List<DateFormat> additionalDateFormats() {
return list(DATE_FORMAT1, DATE_FORMAT2);
}
@Override
public int maxElementsForPrinting() {
return 2000;
}
@Override
public int maxLengthForSingleLineDescription() {
return 150;
}
}
With this custom configuration,
the content of META-INF/services/org.assertj.core.configuration.Configuration must be:
이 사용자 정의 구성에서
META-INF/services/org.assertj.core.configuration.Configuration의 내용은 다음과 같아야 합니다.
example.core.CustomConfiguration
Printing the CustomConfiguration shows:
CustomConfiguration을 출력하면 다음과 같습니다:
Applying configuration example.core.CustomConfiguration
- representation .................................. = BinaryRepresentation
- comparingPrivateFieldsEnabled ................... = false
- extractingPrivateFieldsEnabled .................. = true
- bareNamePropertyExtractionEnabled ............... = false
- lenientDateParsingEnabled ....................... = true
- additionnal date formats ........................ = [yyyy_MM_dd, yyyy|MM|dd]
- maxLengthForSingleLineDescription ............... = 150
- maxElementsForPrinting .......................... = 2000
- removeAssertJRelatedElementsFromStackTraceEnabled = true
Assertions error messages use a Representation to format the different types involved.
There are multiple ways of registering a custom Representation for assertions:
assertion의 에러 메세지의 형식은 타입마다 관련된
Representation을 참고하여 작성됩니다.
사용자Representation을 등록하는 다양한 방법이 있습니다:
Representation by calling Assertions.useRepresentation(myRepresentation) - see Changing the default global scope custom representationRepresentation per assertion with assertThat(actual).withRepresentation(myRepresentation) - see Per assertion scope custom representationConfiguration that specifies the Representation to use - see AssertJ global configuration.
Assertions.useRepresentation(myRepresentation)을 호출함으써Representation의 전역 설정 값을 변경할 수 있습니다. - Changing the default global scope custom representation을 참고하세요.assertThat(actual).withRepresentation(myRepresentation)을 사용해서 assertion마다Representation을 변경할 수 있습니다. - Per assertion scope custom representation을 참고하세요.- 사용할
Representation을 설정한Configuration을 전역적으로 등록할 수 있습니다. - AssertJ global configuration을 살펴보세요.- 사용자 설정을 정의하는 여러 개의 세분화된
Representation을 등록할 수 있습니다.
Let’s go over these different options with a custom Representation.
커스텀
Representation을 사용한 다양한 옵션들을 살펴보겠습니다.
사용자 정의 Representation 만들기
An example of a custom Representation:
커스텀
Representation의 예시:
// dummy class
private class Example {}
public class CustomRepresentation extends StandardRepresentation { // 1 )
// override fallbackToStringOf to handle Example formatting
@Override
public String fallbackToStringOf(Object o) { // 2 )
if (o instanceof Example) return "Example";
// fallback to default formatting.
return super.fallbackToStringOf(o);
}
// override a predefined type formatting : String
// 미리 정의된 형식 재정의 : 문자열
@Override
protected String toStringOf(String str) { // 3 )
return "$" + str + "$";
}
}
1 ) Extends org.assertj.core.presentation.StandardRepresentation to get AssertJ default representation.
2 ) Override fallbackToStringOf and handle your specific types before falling back to the default formatting.
3 ) Change a predefined type formatting by overriding the toStringOf method that takes it as a parameter.
1 )
org.assertj.core.presentation.StandardRepresentation을 상속받아 기본 representation을 가져옵니다.
2 ) 기본 형식으로 돌아가기 전에fallbackToStringOf을 재정의하여 특정 유형을 처리합니다.
3 ) 매개 변수를 사용하는toStringOf메서드를 재정의하여 미리 정의된 형식을 변경합니다.
Let’s see the above custom representation in action when representing Example or String instances.
Example또는String인스턴스를 나타낼 때 위의 사용자 지정 표현이 어떻게 작동하는지 살펴보겠습니다.
This assertion fails ...
아래의 assertion이 실패하면 ...
assertThat(new Example()).isNull();
...with the following error:
아래와 같은 에러 메시지가 나타납니다:
expected:<[null]> but was:<[Example]> // 위의 2 ) 이 적용된 것을 확인할 수 있습니다.
This one fails ...
아래의 검증이 실패하면 ...
// this one fails ...
assertThat("foo").startsWith("bar");
...with the following error:
다음과 같은 에러 메시지가 나타납니다:
Expecting:
<$foo$> // 위의 3 ) 이 적용된 것을 확인할 수 있습니다.
to start with:
<$bar$>
You only have to register CustomRepresentation once but need to do it before executing any tests,
for the tests executed before that, AssertJ will use the default representation.
테스트를 실행하기 전에
CustomRepresentation을 등록해야 합니다.
등록하기 전에 실행된 테스의 경우 AssertJ의 기본 representation을 사용합니다.
// to call before executing tests
// 테스트를 시작하기 전에 useRepresentation을 호출하여 등록해 주세요.
Assertions.useRepresentation(new CustomRepresentation());
Consider writing a JUnit 5 extension implementing BeforeAllCallback to make sure the representation
is set once for all before any test is executed.
테스트가 실행되기 전에 Representation이 한 번씩 설정되도록
BeforeAllCallback을 구현하는 것도 고려해보세요.
Follow this approach if you want to use a specific representation for a single assertion only.
단일 assertion에서만 특정 representation을 사용하려는 경우 다음과 같은 방법을 따르세요.
Example with the failing assertions used before:
Representation customRepresentation = new CustomRepresentation();
// this assertion fails ...
assertThat(new Example()).withRepresentation(customRepresentation) // withRepresentation을 사용하여 등록할 수 있습니다
.isNull();
assertThat("foo").withRepresentation(customRepresentation)
.startsWith("bar");
여러 가지 세분화된 표현 등록하기
Since 3.22.0 AssertJ allows registering multiple representations (one per jar).
The typical use case is for different domain-specific libraries
to be able to independently register Representation implementations for their specific domain objects.
AssertJ 3.22.0부터 여러 개의 representation을 등록할 수 있습니다.
일반적인 사용 예시로는 특정 도메인 개체에 대한 표현을 독립적으로 등록할 수 있는 다양한 도메인별 라이브러리가 있습니다.
In case different representations can represent the same type, the one with the highest priority wins.
서로 다른 표현이 동일한 유형을 나타내는 경우 우선 순위가 높은 것이 적용됩니다.
Let’s take a concrete example where we have two domain specific libraries:
Lotr and star wars and a project that uses them both.
두 개의 도메인별 라이브러리가 있는 것으로 구체적인 예를 살펴보겠습니다.
The Lotr library is composed of an Hobbit class and a specific representation for it,
note that LotrRepresentation represents Hobbits starting with HOBBIT unlike Hobbit toString method:
Lotr 라이브러리는 Hobbit 클래스와 그에 대한 특정 표현으로 구성됩니다.
LotrRepresentation은Hobbit클래스의toString과 달리HOBBIT으로 시작하는Hobbits를 나타냅니다.
package org.assertj.example.lotr;
public class Hobbit {
public String name;
public String age;
@Override
public String toString() {
return format("Hobbit [name=%s, age=%s]", name, age);
}
}
public class LotrRepresentation implements Representation {
@Override
public String toStringOf(Object object) {
if (object instanceof Hobbit) {
Hobbit hobbit = (Hobbit) object;
return String.format("HOBBIT [name=%s, age=%s]", hobbit.name, hobbit.age);
}
return null;
}
// only needed if another library was to represent Hobbit, in this case the one with highest priority wins
@Override
public int getPriority() {
return 5;
}
}
LotrRepresentation is registered by creating a
META-INF/services/org.assertj.core.presentation.Representation file
that contain org.assertj.example.lotr.LotrRepresentation,
the file must be available in the classpath
(typically by putting in it src/main/resources it will end up in the library jar).
LotrRepresentation은org.assertj.example.lotr.LotrRepresentation을 포함하는
META-INF/services/org.assertj.core.presentation.Representation파일을 생성하여 등록합니다.
파일은 클래스 경로에서 사용할 수 있어야 합니다 (일반적으로 src/main/resources에 넣으면 라이브러리 jar에 저장됩니다).
Similarly the star wars library defines a Jedi and a StarWarsRepresentation:
마찬가지로 스타워즈 라이브러리는
Jedi와StarWarsRepresentation을 정의합니다.
package org.assertj.example.starwars;
public class Jedi {
public String name;
public String age;
@Override
public String toString() {
return format("Jedi [name=%s, age=%s]", name, age);
}
}
public class StarWarsRepresentation implements Representation {
@Override
public String toStringOf(Object object) {
if (object instanceof Jedi) {
Jedi jedi = (Jedi) object;
return String.format("JEDI [name=%s, age=%s]", jedi.name, jedi.age);
}
return null;
}
@Override
public int getPriority() {
return 10;
}
}
Same as the Lotr library, StarWarsRepresentation is registered
by creating a META-INF/services/org.assertj.core.presentation.Representation file
that contain org.assertj.example.starwars.StarWarsRepresentation.
Lotr 라이브러리와 동일하게
StarWarsRepresentation은
org.assertj.example.starwars.StarWarsRepresentation을 포함하는
META-INF/services/org.assertj.core.presentation.Representation파일을 생성하여 등록합니다.
The consuming project specifies both libraries as dependencies,
since both have registered a representation, AssertJ will discover them and keep them
in a composite representation that aggregates all registered representaions.
사용할 프로젝트에 두 라이브러리를 종속성으로 지정합니다.
둘 다 representation을 등록했기 때문에 AssertJ는 라이브러리를 검색하고 등록된 모든 representation을 모으는 복합 representation에 보관합니다.
The following test fails with frodo and luke being represented
by LotrRepresentation and StarWarsRepresentation respectively.
다음 테스트는 각각
LotrRepresentation및StarWarsRepresentation에 설정된 표현대로 출력됩니다.
Hobbit frodo = new Hobbit();
frodo.name = "Frodo";
frodo.age = "33";
Jedi luke = new Jedi();
luke.name = "Luke";
luke.age = "23";
assertThat(frodo).isEqualTo(luke);
Error message:
org.opentest4j.AssertionFailedError:
expected: JEDI [name=Luke, age=23]
but was: HOBBIT [name=Frodo, age=33]
iterable 객체 또는 배열의 내용 확인하기
There are different flavors of contains assertion, here’s a table to help choose the most relevant one:
다양한 형태의
containsassertion이 있습니다. 다음은 당신이 가장 관련성이 높은 것을 선택하는 데 도움이 되는 표입니다.
| Assertion | Description |
|---|---|
contains | Verifies that the actual iterable/array contains the given values in any order |
containsOnly | Verifies that the actual group contains only the given values and nothing else in any order and ignoring duplicates (i.e. once a value is found, its duplicates are also considered found) |
containsExactly | Verifies that the actual iterable/array contains exactly the given values and nothing else in order |
containsExactlyInAnyOrder | Verifies that the actual iterable/array contains exactly the given values and nothing else in any order |
containsSequence | Verifies that the actual group contains the given sequence in the correct order and without extra values between the sequence values |
containsSubsequence | Verifies that the actual group contains the given subsequence in the correct order possibly with other values between them |
containsOnlyOnce | Verifies that the actual iterable/array contains the given values only once |
containsAnyOf | Verifies that the actual iterable/array contains at least one of the given values (like an or operator on the given values) |
| Assertion | Description |
|---|---|
contains | 순서에 상관없이 값을 포함하고 있는지 확인합니다. |
containsOnly | 중복은 무시하고 주어진 값만 포함하고 있는지 확인합니다. (즉, 값이 발견되면 중복 된 값도 찾은 것으로 여겨집니다.) |
containsExactly | 순서에 맞게 주어진 값을 정확히 포함하고 있는지 확인합니다. |
containsExactlyInAnyOrder | 순서와 상관없이 주어진 값을 정확히 포함하고 있는지 확인합니다. |
containsSequence | 주어진 그룹을 순서에 맞게 포함하고 있는지 확인합니다. 중간에 다른 객체가 들어가 순서가 달라지면 실패합니다. |
containsSubsequence | 주어진 순서에 맞게 객체가 나열되어 있는지 확인합니다. 중간에 다른 객체가 들어가 있을 수 있습니다. |
containsOnlyOnce | 해당 값을 단 하나만 갖고 있는지 확인합니다. |
containsAnyOf | 주어진 값을 적어도 한 개 이상 포함하고 있는지 확인합니다. |
특정 요소에 대한 assertion 확인하기
Satisfy
만족 여부
You can assert that all or any elements verify the given assertions
with allSatisfy and anySatisfy, conversely noneSatisfy lets you assert
that no elements verify the given assertions.
당신은 모든 요소 또는 일부 요소에 대해
모두 만족,하나라도 만족,하나도 만족하지 않음을
allSatisfy,anySatisfy,noneSatisfy를 통해 확인할 수 있습니다.
The given assertions are expressed with a Consumer (typically with a lambda).
주어진 assertion들은 일반적으로 람다를 사용한
Consumer를 통해 표현됩니다.
Examples:
List<TolkienCharacter> hobbits = list(frodo, sam, pippin);
// all elements must satisfy the given assertions
// 모든 요소들이 아래의 주장을 모두 만족해야 함
assertThat(hobbits).allSatisfy(character -> {
assertThat(character.getRace()).isEqualTo(HOBBIT);
assertThat(character.getName()).isNotEqualTo("Sauron");
});
// at least one element must satisfy the given assertions
// 하나라도 아래의 주장을 모두 만족해야 함
assertThat(hobbits).anySatisfy(character -> {
assertThat(character.getRace()).isEqualTo(HOBBIT);
assertThat(character.getName()).isEqualTo("Sam");
});
// no element must satisfy the given assertions
// 어떤 요소도 아래의 주장을 만족하지 않음
assertThat(hobbits).noneSatisfy(character -> assertThat(character.getRace()).isEqualTo(ELF));
Match
일치 여부
You can assert that all or any elements match the given Predicate with allMatch and anyMatch,
conversely noneMatch lets you assert that no elements verify the given predicate.
주어진
Predicate에 대해 모든 요소 또는 일부 요소가모두 일치,하나라도 일치,하나도 일치하지 않음을
allMatch,anyMatch,noneMatch를 통해 확인할 수 있습니다.
Examples:
List<TolkienCharacter> hobbits = list(frodo, sam, pippin);
assertThat(hobbits).allMatch(character -> character.getRace() == HOBBIT, "hobbits") // 모든 race가 hobbits인지 확인
.anyMatch(character -> character.getName().contains("pp")) // pp라는 이름을 갖는 요소가 1개라도 존재하는지 확인
.noneMatch(character -> character.getRace() == ORC); // race가 ORC인 요소가 하나도 없는지 확인
You can pass a predicate description to make the error message more explicit if the assertion fails.
assertion이 실패할 경우 설명을 전달하여 오류 메시지를 더 명확하게 만들 수 있습니다.
주어진 요소로 이동하기
The idea is to navigate to a given element in order to check it,
you can navigate to the first, last or any element by index or
if you expect only one element use singleElement.
주어진 요소로 이동하여 확인합니다.
첫 번째, 마지막 또는 임의의 인덱스로 이동하거나
단일 요소만 사용하는 경우에 탐색할 수 있습니다.
this is only available for iterables at the moment.
iterables한 경우에만 사용할 수 있습니다.
First / last / element(index)
처음 / 마지막 / 인덱스
Use first, last and element(index) to navigate to the corresponding element,
after navigating you can only use object assertions unless you have specified an Assert class
or preferrably an InstanceOfAssertFactory as shown in the following examples.
first,last그리고element(index)를 사용하여 해당 요소로 이동할 수 있습니다.
탐색 후에는 아래 예제와 같이 Assert 클래스 또는InstanceOfAssertFactory를 지정하지 않는 한 객체에 대한 assertion만 사용할 수 있습니다.
Examples:
// only object assertions available after navigation
// assertion은 navigation 이후에 사용 가능
Iterable<TolkienCharacter> hobbits = list(frodo, sam, pippin);
assertThat(hobbits).first().isEqualTo(frodo); // 첫 번째 객체는 frodo인지 확인
assertThat(hobbits).element(1).isEqualTo(sam); // 인덱스 1에 위치한 요소가 sam인지 확인
assertThat(hobbits).last().isEqualTo(pippin); // 마지막 객체는 pippin인지 확인
// strongly typed String assertions after navigation
// 강한 String assertion의 사용
Iterable<String> hobbitsName = list("frodo", "sam", "pippin");
// STRING is an InstanceOfAssertFactory from org.assertj.core.api.InstanceOfAssertFactories.STRING
// as() is just synthetic sugar for readability
// as()는 가독성을 위해 사용한 것
assertThat(hobbitsName).first(as(STRING))
.startsWith("fro")
.endsWith("do");
assertThat(hobbitsName).element(1, as(STRING))
.startsWith("sa")
.endsWith("am");
assertThat(hobbitsName).last(as(STRING))
.startsWith("pip")
.endsWith("pin");
// alternative for strongly typed assertions
// 강한 assertion의 대안
assertThat(hobbitsName, StringAssert.class).first()
.startsWith("fro")
.endsWith("do");
Single element
단일 객체
singleElement checks that the iterable has only one element and navigates to it,
after navigating you can only use object assertions unless you have specified an Assert class
or preferrably an InstanceOfAssertFactory as shown in the following examples.
singleElement는 iterable에 요소가 하나만 있는지 확인하고 탐색합니다.
탐색 후에는 아래 예제와 같이 Assert 클래스 또는InstanceOfAssertFactory를 지정하지 않는 한 객체에 대한 assertion만 사용할 수 있습니다.
Examples:
Iterable<String> babySimpsons = list("Maggie");
// only object assertions available
assertThat(babySimpsons).singleElement()
.isEqualTo("Maggie");
// to get specific typed assertions, pass the corresponding InstanceOfAssertFactory from
// org.assertj.core.api.InstanceOfAssertFactories.STRING), as() is just synthetic sugar for readability
assertThat(babySimpsons).singleElement(as(STRING))
.endsWith("gie"); // "gie"로 끝나는 String인지 확인
// alternative for strongly typed assertions
assertThat(babySimpsons, StringAssert.class).singleElement()
.startsWith("Mag"); // "Mag"로 시작하는 String인지 확인
Filtering is handy to target assertions on some specific elements, the filter criteria can be expressed by:
필터링은 일부 특정 요소에 대한 assertion을 편리하게 해주며 기준은 다음과 같이 표현할 수 있습니다.
- 자바의 Predicate
- 특정 값을 갖거나 (갖지 않거나) 일련의 값을 가지는 (갖지 않는) 요소 속성/필드
- null 값을 갖는 특정 요소 속성/필드
- 일부 assertion과 일치하는 요소
- 조건과 일치하는 요소
Let’s explore these options in some examples taken from FilterExamples from the assertions-examples project.
이러한 옵션들을 몇 가지 예시와 함께 알아보겠습니다.
Filtering with a Predicate
You specify the filter condition using simple predicate, best expressed with a lambda.
당신은 predicate를 사용한 람다를 통해 필터 조건을 지정할 수 있습니다.
Example:
assertThat(fellowshipOfTheRing).filteredOn( character -> character.getName().contains("o") ) // 람다 사용
.containsOnly(aragorn, frodo, legolas, boromir);
Filtering on a property or a field
First you specify the property/field name to filter on and then its expected value.
The filter first tries to get the value from a property, then from a field.
Reading private fields is supported by default,
but can be disabled globally by calling Assertions.setAllowExtractingPrivateFields(false).
먼저 필터링할 속성/필드 이름을 지정한 다음에 그것의 예상 값을 지정합니다.
필터는 먼저 속성에서 값을 가져온 다음에 필드에서 값을 가져오려고 시도합니다.
기본적으로 private 필드를 읽어들이는 것은 지원하는 것으로 설정되어 있으나,
Assertions.setAllowExtractingPrivateFields(false)을 통해 전역적으로 비활성화할 수 있습니다.
Filter supports nested properties/fields.
Note that if an intermediate value is null the whole nested property/field is considered to be null,
for example reading "address.street.name" will return null if "address.street" is null.
Filter는 중첩된 속성/필드를 지원합니다.
중간 값이 null이면 중첩된 속성/필드 전체가 null로 간주됩니다.
예를 들어"address.street.name"을 읽을 때"address.street"가 null이면 null이 반환됩니다.
Filters support these basic operations : not, in, notIn
필터는 다음과 같은 기본 작업을 지원합니다 :
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);
// nested properties are supported
// `"race.name"` 과 같은 중첩된 속성을 지원합니다.
assertThat(fellowshipOfTheRing).filteredOn("race.name", "Man")
.containsOnly(aragorn, boromir);
// you can apply different comparison
// `not`, `in`, `notIn`을 사용하여 조건을 다르게 변경할 수 있습니다.
assertThat(fellowshipOfTheRing).filteredOn("race", notIn(HOBBIT, MAN))
.containsOnly(gandalf, gimli, legolas);
assertThat(fellowshipOfTheRing).filteredOn("race", in(MAIA, MAN))
.containsOnly(gandalf, boromir, aragorn);
assertThat(fellowshipOfTheRing).filteredOn("race", not(HOBBIT))
.containsOnly(gandalf, boromir, aragorn, gimli, legolas);
// you can chain multiple filter criteria
// 여러 개의 필터 기준을 체이닝할 수 있습니다.
assertThat(fellowshipOfTheRing).filteredOn("race", MAN)
.filteredOn("name", not("Boromir"))
.containsOnly(aragorn);
Filtering on a function return value
함수의 반환 값을 필터링
This is a more flexible way of getting the value to filter on but note
that there is no support for operators like not, in and notIn.
이것은 필터링된 값을 얻는 보다 유연한 방법이지만,
not,in그리고notIn과 같은 연산자를 지원하지 않습니다.
assertThat(fellowshipOfTheRing).filteredOn(TolkienCharacter::getRace, HOBBIT)
.containsOnly(sam, frodo, pippin, merry);
Filtering on null value
null 값 필터링
Filters the elements whose specified property/field is null.
속성/필드가 null인 요소를 필터링합니다.
Filter supports nested properties/fields.
Note that if an intermediate value is null the whole nested property/field is considered to be null,
for example reading "address.street.name" will return null if "address.street" is null.
필터는 중첩된 속성/필드를 지원합니다.
중간 값이 null인 경우 전체의 속성/필드가 null인 것으로 간주됩니다.
예를 들어"address.street.name"을 읽을 때,"address.street"이 null이라면 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"))
.singleElement()
.isEqualTo(mysteriousHobbit);
Filtering elements matchin given assertions
지정된 assertion과 일치하는 필터링 요소
Filters the iterable under test keeping only elements matching
the given assertions specified with a Consumer.
테스트 중인 iterable을 필터링하여 Consumer로 지정된 assertion과 일치하는 요소만 유지합니다.
Example: check hobbits whose age < 34
예: hobbit들 중에서 나이가 34살 미만인 경우만 남기기
TolkienCharacter pippin = new TolkienCharacter("Pippin", 28, HOBBIT);
TolkienCharacter frodo = new TolkienCharacter("Frodo", 33, HOBBIT);
TolkienCharacter merry = new TolkienCharacter("Merry", 36, HOBBIT);
TolkienCharacter sam = new TolkienCharacter("Sam", 38, HOBBIT);
List<TolkienCharacter> hobbits = list(frodo, sam, merry, pippin);
assertThat(hobbits).filteredOnAssertions(hobbit -> assertThat(hobbit.age).isLessThan(34))
.containsOnly(frodo, pippin);
Filtering with a Condition
조건에 맞는 필터링
Filter the iterable/array under test keeping only elements matching the given Condition.
주어진 조건과 일치하는 요소만 유지하면서 테스트 중인 iterable/array를 필터링합니다.
Two methods are available : being(Condition) and having(Condition).
They do the same job - pick the one that makes your code more readable!
2가지 메서드를 사용할 수 있습니다 : 1. being과 2. having
2가지 메서드는 같은 작업을 수행합니다 - 코드를 더 읽기 쉽게 만드는 것을 선택하세요!
import org.assertj.core.api.Condition;
Condition<Player> mvpStats= new Condition<Player>(player -> {
// 게임 당 점수가 20점 이상이면서 게임 당 어시스트가 8점 이상이거나 게임 당 리바운드가 8점 이상인 선수만 남겨서 반환합니다.
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);
What problem extracting solves
Let’s say you have called some service and got a list (or an array) of TolkienCharacter,
to check the results you have to build the expected TolkienCharacters, that can be quite tedious!
서비스를 호출하여
TolkienCharacter의 리스트(또는 배열)을 얻었다고 가정해 보겠습니다.
그리고 결과를 확인하려면 예상되는TolkienCharacter를 빌드해야 합니다.
상당히 지루한 작업이죠!
List<TolkienCharacter> fellowshipOfTheRing = tolkienDao.findHeroes(); // frodo, sam, aragorn ...
// requires creation of frodo and aragorn, the expected TolkienCharacters
assertThat(fellowshipOfTheRing).contains(frodo, aragorn);
Instead, it is usually enough to check some fields or properties on the elements,
for that you have to extract the fields/properties before performing your assertions, something like:
대신, 일반적으로 다음과 같이 assertion을 수행하기 전에 필드/속성을 추출하기 때문에
요소의 일부 필드 또는 속성을 확인하는 것으로 충분히 검사할 수 있습니다.
// extract the names ...
List<String> names = fellowshipOfTheRing.stream().map(TolkienCharacter::getName).collect(toList());
// ... and finally assert something
assertThat(names).contains("Boromir", "Gandalf", "Frodo", "Legolas");
This is too much work (even with the stream API), instead AssertJ can help extracting values
from the elements under tests, there are several ways of doing so:
이것은 너무 많은 작업을 필요로 합니다. (stream API를 사용하는 경우에도)
대신 AssertJ는 테스트 중인 요소에서 값을 추출하는 데 도움이 되는 몇 가지 방법을 제공합니다:
- 요소에서 단일 값 추출
- 요소에서 여러 값 추출
- 요소에서 여러 값 추출 및 평면화
Extracting single value per element
Specify the field/property to extract (or pass a Function)
from each elements and perform assertions on the extracted values.
각 요소에서 추출(또는 함수 전달)할 필드/속성을 지정하고 추출된 값에 대한 assertion을 수행합니다.
Extracting by name can access private fields/properties
which is handy to check internals not exposed with public methods (lambda won’t work here),
it also supports nested field/property like "race.name".
이름으로 추출하면 public 메서드로 노출되지 않은 내부를 확인하는 데 편리한 private 필드/속성에 접근할 수 있으며(여기서 lambda는 작동하지 않음)
"race.name"과 같은 중첩된 필드/속성도 지원합니다.
Examples:
// "name" needs to be either a property or a field of the TolkienCharacter class
// "name"은 TolkienCharacter 클래스의 속성 또는 필드여야 합니다.
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:
// map을 사용한 예시
assertThat(fellowshipOfTheRing).map(TolkienCharacter::getName)
.contains("Boromir", "Gandalf", "Frodo", "Legolas");
Note that extracting one property can be made strongly typed by
giving the property type as the second argument.
두 번째 인자로 속성의 타입을 지정함으로써 강력한 형식으로 하나의 속성을 추출할 수 있습니다.
// to have type safe extracting, use the second parameter to pass the expected property type:
// type-safe한 추출을 위해 두번째 인자로 예상하는 타입을 지정할 수 있습니다:
assertThat(fellowshipOfTheRing).extracting("name", String.class) // String type 검증
.contains("Boromir", "Gandalf", "Frodo", "Legolas")
.doesNotContain("Sauron", "Elrond");
Extracting multiple values
You can extract several values from the elements under test and check them using tuples.
테스트 중인 요소에서 여러 값을 추출하고 튜플을 사용하여 확인할 수 있습니다.
As an example, let’s check the name, age and race’s name of each TolkienCharacter element:
예를 들어, 각 TolkienCharacter 요소의 이름, 나이, 인종 이름을 확인해 보겠습니다.
// when checking several properties/fields you have to use tuples:
// 여러 속성/필드를 확인할 때는 튜플을 사용해야 합니다:
import static org.assertj.core.api.Assertions.tuple;
// extracting name, age and and race.name nested property
assertThat(fellowshipOfTheRing).extracting("name", "age", "race.name")
.contains(tuple("Boromir", 37, "Man"),
tuple("Sam", 38, "Hobbit"),
tuple("Legolas", 1000, "Elf"));
// same assertion with functions for type safety:
// 함수를 사용하여 type-safety하게 같은 assertion 수행
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"));
The extracted name, age and race’s name values of the current element are grouped in a tuple,
thus you need to use tuples for specifying the expected values.
추출된 현재 요소의 이름, 나이, 인종 이름 값은 튜플로 그룹화되어 있으므로 튜플을 사용하여 예상 값을 지정해야 합니다.
More examples are available in IterableAssertionsExamples.java of the assertj-examples project.
더 많은 예시는 IterableAssertionsExamples.java of the assertj-examples project에서 확인할 수 있습니다.
Extracting and flattening multiple values per element
Flat extracting is hard to explain but easy to understand with an example,
so let’s see how it works (in functional programming it is juts a flatMap).
플랫 추출은 설명보다 예제를 통해 이해하기 쉬우므로 어떻게 작동하는지 예시를 통해 살펴보겠습니다.(함수형 프로그래밍에서는 플랫맵을 사용합니다)
Let’s assume we have a Player class with a teamMates property returning a
List<Player> and we want to assert that it returns the expected players:
List<Player>를 반환하는 teamMates 속성이 있는 Player 클래스가 있고 예상 플레이어를 반환한다고 주장을 가정해 보겠습니다.
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):
// teamMates 속성을 지정하여 모든 팀원을 확인합니다(플레이어에는 getTeamMates() 메서드가 있음):
assertThat(reallyGoodPlayers).flatExtracting("teamMates")
.contains(pippen, kukoc, jabbar, worthy);
// alternatively, you can use a Function for type safety:
// 또는, type-safety를 위해서 함수를 사용할 수도 있습니다:
assertThat(reallyGoodPlayers).flatExtracting(BasketBallPlayer::getTeamMates)
.contains(pippen, kukoc, jabbar, worthy);
// flatMap is an alias of flatExtracting:
// flatMap은 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:
// flatExtracting 대신 추출을 사용하면 결과는 플레이어 목록이 되므로 assertion은 다음과 같이 됩니다:
assertThat(reallyGoodPlayers).extracting("teamMates")
.contains(list(pippen, kukoc), list(jabbar, worthy));
You can use flatMap in place of flatExtracting (except for the variant taking a String)
flatExtracting 대신 flatMap을 사용할 수 있습니다.(문자열을 사용하는 경우 제외)
Flat extracting can be used to group multiple values if you don’t want to use extracting and tuples:
extracting과 튜플을 사용하지 않으려면 플랫 추출을 사용하여 여러 값을 그룹화할 수 있습니다.
// extract a list of values, flatten them and use contains assertion
// 값들의 리스트를 추출한 다음, contains assertion을 사용하여 평명화합니다.
assertThat(fellowshipOfTheRing).flatExtracting("name", "race.name")
.contains("Frodo", "Hobbit", "Legolas", "Elf");
// same assertions with Functions:
// 함수를 사용한 같은 assertion:
assertThat(fellowshipOfTheRing).flatExtracting(TolkienCharacter::getName,
tc -> tc.getRace().getName())
.contains("Frodo", "Hobbit", "Legolas", "Elf");
usingElementComparator allows you to change the way elements are compared
(instead of using the elements equals method).
usingElementComparator를 사용하여 요소를 비교하는 방식을 변경할 수 있습니다. (equals메서드를 사용하는 대신)
Examples:
List<TolkienCharacter> fellowshipOfTheRing = list(frodo, sam, merry, pippin, gandald, legolas, boromir, aragorn, gimli);
// the fellowshipOfTheRing includes Gandalf but not Sauron ...
// fellowshipOfTheRing이 Gandalf는 포함하고 Sauron은 포함하지 않을 때 ...
assertThat(fellowshipOfTheRing).contains(gandalf)
.doesNotContain(sauron);
// ... but if we compare only races, Sauron is in fellowshipOfTheRing since he's a Maia like Gandalf
// ... 하지만 종족만 비교한다면 Sauron은 Gandalf와 같은 Maia이므로 FellowshipOfTheRing에 있음을 확인할 수 있습니다.
assertThat(fellowshipOfTheRing).usingElementComparator((t1, t2) -> t1.getRace().compareTo(t2.getRace()))
.contains(sauron);
This chapter answers the question: how to assert that an exception has been thrown
and check that it is the expected one?
이번 챕터는 다음과 같은 질문에 대해 답합니다: 어떻게 예외가 발생했음을 검증하고 예상한 예외인지 확인할 수 있을까?
예외 메시지 확인하기
There are various ways for checking the exception message content,
you can check the exact message, what it contains, its start, its end, if it matches a regex.
예외 메시지의 내용을 확인하는 다양한 방법이 존재합니다.
여러분은 정확한 메시지를 확인할 수도, 메시지가 포함하고 있는 것을 확인할 수도, 메시지가 어떻게 시작하는지, 어떻게 끝나는지 혹은 정규표현식과 일치하는지도 확인할 수 있습니다.
Most of the assertions expecting a String can use the String.format syntax.
대부분의
String검증은 String.format을 사용하여 할 수 있습니다.
Examples:
Throwable throwable = new IllegalArgumentException("wrong amount 123");
assertThat(throwableWithMessage).hasMessage("wrong amount 123")
.hasMessage("%s amount %d", "wrong", 123)
// check start
// 시작 부분 확인
.hasMessageStartingWith("wrong")
.hasMessageStartingWith("%s a", "wrong")
// check content
// 포함 여부 확인
.hasMessageContaining("wrong amount")
.hasMessageContaining("wrong %s", "amount")
.hasMessageContainingAll("wrong", "amount")
// check end
// 끝 부분 확인
.hasMessageEndingWith("123")
.hasMessageEndingWith("amount %s", "123")
// check with regex
// 정규표현식 확인
.hasMessageMatching("wrong amount .*")
// check does not contain
// 미포함 여부 확인
.hasMessageNotContaining("right")
.hasMessageNotContainingAny("right", "price")
There are two approaches to check the cause and root cause,
either directly or navigate to it with getCause() and getRootCause() and check it.
getCause()와getRootCause()을 사용하여 원인과 근본적인 원인을 확인하는 두 가지 접근법이 있습니다.
Checking the cause
You can check the cause directly if you know it but that’s not always possible,
in that case you can basically only check its type. This is pretty limited in terms of assertions,
a better approach is to navigate to the cause with getCause()
and take advantage of all existing exception assertions.
Direct cause assertion are limited ...
만약 당신이 원인을 알고 있다면 직접 확인할 수 있겠지만, 항상 가능한 것은 아니며 기본적으로 타입만 확인할 수 있습니다.
상당히 제한적인 검증 방법 대신getCause()를 사용하여 원인을 탐색하고 제공하는 모든 예외 assertion을 활용하는 것을 권장합니다.
직접적인 원인 검증은 제한적입니다 ...
NullPointerException cause = new NullPointerException("boom!");
Throwable throwable = new Throwable(cause);
assertThat(throwable).hasCause(cause)
// hasCauseInstanceOf will match inheritance.
// 상속과 일치
.hasCauseInstanceOf(NullPointerException.class)
.hasCauseInstanceOf(RuntimeException.class)
// hasCauseExactlyInstanceOf will match only exact same type
// 정확히 같은 타입인지 검증
.hasCauseExactlyInstanceOf(NullPointerException.class);
... but navigating to the cause allows to take advantage of all exception assertions:
그러나 원인을 탐색하면 모든 예외 관련 assertion을 활용할 수 있습니다:
// navigate before checking
assertThat(throwable).getCause()
.hasMessage("boom!")
.hasMessage("%s!", "boom")
.hasMessageStartingWith("bo")
.hasMessageEndingWith("!")
.hasMessageContaining("boo")
.hasMessageContainingAll("bo", "oom", "!")
.hasMessageMatching("b...!")
.hasMessageNotContaining("bam")
.hasMessageNotContainingAny("bam", "bim")
// isInstanceOf will match inheritance.
// 상속과 일치
.isInstanceOf(NullPointerException.class)
.isInstanceOf(RuntimeException.class)
// isExactlyInstanceOf will match only exact same type
// 정확히 같은 타입인지 검증
.isExactlyInstanceOf(NullPointerException.class);
An alternative is using assertThatExceptionOfType combined with havingCause
as shown in the following example:
다음 예제와 같이
havingCause와 함께assertThatExceptionOfType을 사용하여 검증할 수도 있습니다:
assertThatExceptionOfType(RuntimeException.class)
.isThrownBy(() -> { throw new RuntimeException(new IllegalArgumentException("boom!")); })
.havingCause() // 발생 원인의
.withMessage("boom!"); // 예외 메시지
Checking the root cause
근본 원인을 확인하기
You can check the root cause directly with hasRootCause,
hasRootCauseMessage and hasRootCauseInstanceOf if you have access to it but that’s not always possible,
this is a bit limited in terms of assertions,
a better way is to navigate to the root cause with getRootCause()
and take advantage of all existing exception assertions.
당신이 접근 가능하다면
hasRootCause,hasRootCauseMessage,hasRootCauseInstanceOf를 사용하여 바로 근본 원인을 확인할 수 있습니다만, 항상 가능한 것은 아닙니다.
이는 다소 제한적인 검증 방법입니다.
근본 원인에 접근하는 더 나은 방법은getRootCause()를 사용하는 것입니다.
getRootCause()를 사용하면 제공하는 모든 assertion의 이점을 활용할 수 있습니다.
Examples:
NullPointerException rootCause = new NullPointerException("null!"); // 에러 예시
Throwable throwable = new Throwable(new IllegalStateException(rootCause));
// direct root cause check
// 직접 원인 확인하기
assertThat(throwable).hasRootCause(rootCause)
.hasRootCauseMessage("null!")
.hasRootCauseMessage("%s!", "null")
// hasRootCauseInstanceOf will match inheritance
// 해당 클래스의 객체인지 확인 (상속 관계 확인)
.hasRootCauseInstanceOf(NullPointerException.class)
.hasRootCauseInstanceOf(RuntimeException.class)
// hasRootCauseExactlyInstanceOf will match only exact same type
// 정확한 예외 클래스 확인
.hasRootCauseExactlyInstanceOf(NullPointerException.class);
// navigate to root cause and check
// 근본 원인으로 이동하여 확인 (더 다양한 검증 메서드를 활용할 수 있음)
assertThat(throwable).getRootCause()
.hasMessage("null!")
.hasMessage("%s!", "null")
.hasMessageStartingWith("nu")
.hasMessageEndingWith("!")
.hasMessageContaining("ul")
.hasMessageContainingAll("nu", "ull", "l!")
.hasMessageMatching("n...!")
.hasMessageNotContaining("NULL")
.hasMessageNotContainingAny("Null", "NULL")
// isInstanceOf will match inheritance.
// 해당 클래스의 객체인지 확인 (상속 관계 확인)
.isInstanceOf(NullPointerException.class)
.isInstanceOf(RuntimeException.class)
// isExactlyInstanceOf will match only exact same type
// 정확한 예외 클래스 확인
.isExactlyInstanceOf(NullPointerException.class);
An alternative is using assertThatExceptionOfType combined with havingRootCause
as shown in the following example:
대안으로는 다음 예시와 같이
assertThatExceptionOfType와havingRootCause를 함께 사용하는 방법이 있습니다.
assertThatExceptionOfType(RuntimeException.class)
.isThrownBy(() -> { throw new RuntimeException(new IllegalArgumentException(new NullPointerException("root error"))); })
.havingRootCause()
.withMessage("root error");
No cause
원인이 없는 경우
You can verify that a Throwable does not have a cause with hasNoCause().
예외가 근본 원인을 갖고 있지 않은 경우
hasNoCause()를 사용하여 확인할 수 있습니다.
BDD aficionados can separate WHEN and THEN steps by using catchThrowable(ThrowingCallable)
to capture the Throwable and then perform assertions (ThrowingCallable is a functional interface
which can be expressed as a lambda).
BDD 애호가라면,
Throwable을 잡아 검증을 수행하는catchThrowable(ThrowingCallable)을 사용하여 WHEN, THEN과 같이 스탭을 나눌 수 있습니다.
ThrowingCallable은 람다를 표현하기 위한 함수형 인터페이스입니다.
Example:
// GIVEN
String[] names = { "Pier ", "Pol", "Jak" };
// WHEN
Throwable thrown = catchThrowable(() -> System.out.println(names[9])); // 발생한 예외를 잡아서 변수에 할당 : WHEN을 작성할 수 있게 됩니다.
// THEN
then(thrown).isInstanceOf(ArrayIndexOutOfBoundsException.class)
.hasMessageContaining("9");
// assertThat is also available but is less "BDD style"
assertThat(thrown).isInstanceOf(ArrayIndexOutOfBoundsException.class)
.hasMessageContaining("9");
catchThrowable returns null if no exception is thrown,
but there is a better way to check that no exception is thrown.
만약 던져진 예외가 없다면
catchThrowable은 null을 반환합니다.
하지만 아무 예외도 발생하지 않음을 확인하는 더 나은 방법도 존재합니다.
catchThrowableOfType is a variation of catchThrowable where the caught exception type is
verified and returned allowing to check the custom exception fields/properties.
catchThrowableOfType은 잡은 예외의 타입을 확인하고 사용자 정의 예외 필드/속성을 확인한 뒤 반환하는catchThrowable의 변형입니다.
assertThatThrownBy(ThrowingCallable) is an alternative to catchThrowable,
use it if you find more readable.
assertThatThrownBy(ThrowingCallable)은catchThrowable보다 가독성이 좋은 대체 가능한 메서드입니다.
Example:
assertThatThrownBy(() -> { throw new Exception("boom!"); }).isInstanceOf(Exception.class) // 예외 발생 시 타입 검증
.hasMessageContaining("boom"); // 예외 메시지 검증
If the provided ThrowingCallable does not raise an exception, an assertion error is immediately thrown.
만약
ThrowingCallable이 예외를 발생시키지 않으면 즉시 검증 오류가 발생합니다.
assertThatExceptionOfType is an alternative syntax that some people find more natural.
assertThatExceptionOfType은 일부 사용자들이 더 자연스럽게 사용하는 대체 가능한 메서드입니다.
assertThatExceptionOfType(IOException.class).isThrownBy(() -> { throw new IOException("boom!"); })
.withMessage("%s!", "boom")
.withMessageContaining("boom")
.withNoCause();
If the provided ThrowingCallable does not raise an exception, an assertion error is immediately thrown.
만약
ThrowingCallable이 예외를 발생시키지 않으면 즉시 검증 오류가 발생합니다.
Similarly to catchThrowableOfType, the latter syntax has been enriched for commonly used exceptions:
catchThrowableOfType과 유사하게 다음의 구문들은 사용하여 예외 검증을 보다 강화할 수 있습니다.
assertThatNullPointerExceptionassertThatIllegalArgumentExceptionassertThatIllegalStateExceptionassertThatIOExceptionThe previous example can be rewritten as:
이전 예제는 다음과 같이 다시 작성할 수 있습니다:
assertThatIOException().isThrownBy(() -> { throw new IOException("boom!"); }) // assertThatIOException()의 사용으로 강화된 검증
.withMessage("%s!", "boom")
.withMessageContaining("boom")
.withNoCause();
You can test that a piece of code does not throw any exception with:
다음을 사용하여 코드가 예외를 던지지 않는지 확인할 수 있습니다:
// standard style
assertThatNoException().isThrownBy(() -> System.out.println("OK"));
// BDD style
thenNoException().isThrownBy(() -> System.out.println("OK"));
or similarly:
또는 다음과 같이 작성할 수도 있습니다:
// standard style
assertThatCode(() -> System.out.println("OK")).doesNotThrowAnyException(); // 어떠한 예외도 발생하지 않음
// BDD style
thenCode(() -> System.out.println("OK")).doesNotThrowAnyException();
With soft assertions AssertJ collects all assertion errors instead of stopping at the first one.
soft assertions AssertJ를 사용하면 하나의 검증이 틀렸을 때 멈추지 않고 모든 검증 로직을 실행합니다.
This is especially useful for long tests like end-to-end tests
as we can fix all reported errors at once and avoid multiple failing runs.
이것은 여러 번의 실패를 반복하지 않고 모든 오류를 한번에 수정할 수 있으므로 end-to-end 테스트와 같은 긴 테스트에 특히 유용합니다.
Since soft assertions don’t fail at the first error,
you need to tell AssertJ when to report the captured assertion errors,
there are different ways of doing so:
soft assertions은 첫 번째 실패에 멈추지 않기 때문에, 잡아낸 assertion 오류를 보고할 시기를 AssertJ에게 알려야합니다. 이를 설정하는 방법은 여러가지가 있습니다:
assertAll() (basic approach)assertAll() after each testsSoftAssertions or a BDDSoftAssertions parameter and calls assertAll() after each testsAutoCloseableSoftAssertionsassertSoftly static method
assertAll()을 호출하는 것 (기본적인 접근 방법)- 각 테스트 진행 후
assertAll()을 호출하는 Junit4의 규칙 사용SoftAssertions또는BDDSoftAssertions를 매개변수로 주입하고 각 테스트 후에 assertAll()을 호출하는 Junit5에서 제공하는 확장을 사용AutoCloseableSoftAssertions을 사용하는 방법assertSoftly정적 메서드를 사용하는 방법
Soft assertions comes with a BDD flavor where assertThat is replaced by then.
Soft assertions은
assertThat이then으로 대체되는 BDD 스타일도 함께 제공합니다.
If you have created your own custom Soft assertions
it is possible to combine them all in a single soft assertions entry point.
만약 사용자 정의 Soft assertions을 만들었다면 단일 검증의 진입 포인트에서 모두 결합할 수 있습니다.
Let’s see first how to use soft assertions requiring an explicit call to assertAll(),
the other approaches that don’t require this explicit call are described in the subsequent sections.
먼저
assertAll()에 대한 명시적인 호출이 필요한 soft assertion을 사용하는 방법을 살펴보겠습니다.
이러한 명시적 호출이 필요없는 다른 방법은 이어지는 다음 파트에서 설명합니다.
Example:
@Test
void basic_soft_assertions_example() {
SoftAssertions softly = new SoftAssertions(); // soft assertion 인스턴스 생성
softly.assertThat("George Martin").as("great authors").isEqualTo("JRR Tolkien"); // 검증 추가 1
softly.assertThat(42).as("response to Everything").isGreaterThan(100); // 검증 추가 2
softly.assertThat("Gandalf").isEqualTo("Sauron"); // 검증 추가 3
// Don't forget to call assertAll() otherwise no assertion errors are reported!
// assertAll()을 호출하여 결과를 보고 받는 것을 잊지마세요!
softly.assertAll(); // assertAll 호출
}
SoftAssertions instance to record all assertion errorssoftly.assertThat instead of the usual assertThat methodsassertAll() to report all assertion errors!The previous test fails with the message below reporting all the errors:
위의 테스트는 모든 오류를 보고하고 실패합니다.
Multiple Failures (3 failures)
-- failure 1 --
[great authors]
Expecting:
<"George Martin">
to be equal to:
<"JRR Tolkien">
but was not.
-- failure 2 --
[response to Everything]
Expecting:
<42>
to be greater than:
<100>
-- failure 3 --
Expecting:
<"gandalf">
to be equal to:
<"sauron">
but was not.
BDD는 "Behavior-Driven Development"의 약어로, 행위 주도 개발을 의미합니다.
BDD aficionados can use BDD soft assertions where assertThat is replaced by then.
BDD 스타일을 좋아한다면
assertThat대신then을 사용할 수 있습니다.
Example:
@Test
void basic_bdd_soft_assertions_example() {
BDDSoftAssertions softly = new BDDSoftAssertions(); // BDDSoftAssertion 인스턴스 생성
softly.then("George Martin").as("great authors").isEqualTo("JRR Tolkien"); // `assertThat` 대신 `then` 사용
softly.then(42).as("response to Everything").isGreaterThan(100);
softly.then("Gandalf").isEqualTo("Sauron");
// Don't forget to call assertAll() otherwise no assertion errors are reported!
// 마찬가지로 assertAll()을 호출하는 것을 잊지마세요!
softly.assertAll();
}
There are BDD soft assertions versions for the different soft assertions approaches:
다양한 soft assertion에 따른 다양한 BDD 스타일의 soft assertion도 존재합니다:
AutoCloseableBDDSoftAssertionsJUnitBDDSoftAssertions that takes care of calling assertAll() after each testsassertAll() after each tests
AutoCloseableBDDSoftAssertions- 각 테스트 후
assertAll()을 호출하는JUnitBDDSoftAssertions사용- 각 테스트 후
assertAll()호출을 처리하는 Junit5의 확장 사용
The JUnit rule provided by AssertJ takes care of calling assertAll() at the end of each test.
AssertJ에서 제공하는 Junit rule은 각 테스트가 끝날 때
assertAll()을 호출하여 처리합니다.
Example:
@Rule // Rule 애너테이션 사용
public final JUnitSoftAssertions softly = new JUnitSoftAssertions();
@Test
void junit4_soft_assertions_example() {
softly.assertThat("George Martin").as("great authors").isEqualTo("JRR Tolkien");
softly.assertThat(42).as("response to Everything").isGreaterThan(100);
softly.assertThat("Gandalf").isEqualTo("Sauron");
// No need to call softly.assertAll(), this is automatically done by the JUnitSoftAssertions rule
// JUnitSoftAssertions rule에 의해 자동으로 처리되기 때문에 assertAll을 호출할 필요가 없습니다.
}
In a similar way you can use JUnitBDDSoftAssertions where assertThat is replaced by then:
유사한 방식으로
assertThat을then으로 대체할 수 있는JUnitBDDSoftAssertions을 사용할 수도 있습니다.
@Rule // Rule 애너테이션 사용
public final JUnitBDDSoftAssertions softly = new JUnitBDDSoftAssertions();
@Test
void junit4_bdd_soft_assertions_example() {
softly.then("George Martin").as("great authors").isEqualTo("JRR Tolkien");
softly.then(42).as("response to Everything").isGreaterThan(100);
softly.then("Gandalf").isEqualTo("Dauron");
// No need to call softly.assertAll(), this is automatically done by the JUnitSoftAssertions rule
// JUnitSoftAssertions rule에 의해 자동으로 처리되기 때문에 assertAll을 호출할 필요가 없습니다.
}
SoftAssertionsExtension is a JUnit 5 extension that:
SoftAssertionsExtension은 Junit 5의 확장으로:
assertAll() at the end of each testsSoftAssertionsProvider field annotated with @InjectSoftAssertionsSoftAssertionsProvider parameter in each test methods
- 각 테스트가 끝날 때 자동으로
assertAll()을 호출하여 처리합니다.@InjectSoftAssertions을 사용하여SoftAssertionsProvider의 필드를 초기화할 수 있습니다.- 각 테스트 메서드에
SoftAssertionsProvider매개변수 주입을 지원합니다.
SoftAssertionsProvider is the interface that any concrete soft assertions class must implement,
AssertJ provides two of them: SoftAssertions and BDDSoftAssertions,
but custom implementations are also supported as long as they have a default constructor.
See the end of combining soft assertions entry points section for an example.
SoftAssertionsProvider은 구체적인 soft assertion 클래스가 구현해야 하는 인터페이스입니다.
AssertJ는 다음 두 가지를 제공합니다:SoftAssertions과BDDSoftAssertions
그러나 사용자 정의 구현도 기본 생성자가 있다면 지원 가능합니다.
예제는 combining soft assertions entry points 파트의 끝 부분을 참고하세요.
JUnitJupiterSoftAssertions,JUnitJupiterBDDSoftAssertionsandSoftlyExtension
are now deprecated in favor ofSoftAssertionsExtension.
JUnitJupiterSoftAssertions,JUnitJupiterBDDSoftAssertions그리고SoftlyExtension은
SoftAssertionsExtension을 위해 더 이상 사용하지 않습니다. (deprecated)
SoftAssertionsProvider field injection
SoftAssertionsProvider 필드 주입
SoftAssertionsExtension supports injecting any instance of SoftAssertionsProvider
into a class test field annotated with @InjectSoftAssertions.
The injection occurs before each test method execution,
after each test assertAll() is invoked to verify that no soft assertions failed.
SoftAssertionsExtension은SoftAssertionsProvider의 어떠한 인스턴스든지
@InjectSoftAssertions애너테이션이 붙은 테스트 클래스의 필드에 주입하는 것을 지원합니다.
주입은 각각의 테스트 메서드가 실행되기 전과
assertAll()이 호출되어 실패한 soft assertion이 없는지 확인하고 발생합니다.
A nested test class can provide a SoftAssertionsProvider field
when it extends this extension or can inherit the parent’s one.
중첩된 테스트 클래스는 이 확장을 extends 하거나 부모의 확장을 상속할 때
SoftAssertionsProvider필드를 사용할 수 있습니다.
You can have multiple soft assertion providers injected into a single test class.
Assertions made on any of them will be collected in a single error collector and reported all together,
in the same order that they failed.
단일 테스트 클래스에 여러 개의 soft assertion provider를 주입할 수 있습니다.
하나의 에러 수집기에 함께 집계되고 결과 또한 동일한 순서로 함께 보고됩니다.
This extension throws an ExtensionConfigurationException if:
이 확장은 다음과 같은 경우
ExtensionConfigurationException을 발생시킵니다:
SoftAssertionsProvider (or subclass); or
- 필드가 static 또는 final이거나 접근할 수 없는 경우
- 필드 type이
SoftAssertionsProvider(또는 하위 클래스)의 구체적인 구현이 아닌 경우- 기본 생성자가 없는 경우
Example:
import org.assertj.core.api.SoftAssertions;
import org.assertj.core.api.junit.jupiter.SoftAssertionsExtension;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@ExtendWith(SoftAssertionsExtension.class) // Junit5 확장 사용
public class JUnit5SoftAssertionsExtensionAssertionsExamples {
@InjectSoftAssertions
private SoftAssertions softly;
@Test
public void chained_soft_assertions_example() {
String name = "Michael Jordan - Bulls";
softly.assertThat(name).startsWith("Mi")
.contains("Bulls");
// no need to call softly.assertAll(), this is done by the extension
// 확장에 의해서 처리되기 때문에 assertAll()을 호출할 필요가 없습니다.
}
// nested classes test work too
// 중첩된 클래스 또한 테스트 가능합니다.
@Nested
class NestedExample {
@Test
public void football_assertions_example() {
String kylian = "Kylian Mbappé";
softly.assertThat(kylian).startsWith("Ky")
.contains("bap");
// no need to call softly.assertAll(), this is done by the extension
// 확장에 의해서 처리되기 때문에 assertAll()을 호출할 필요가 없습니다.
}
}
}
SoftAssertionsProvider parameter injection
SoftAssertionsProvider 파라미터 주입
SoftAssertionsExtension supports injecting any SoftAssertionsProvider implementation
as a parameter in any test method.
SoftAssertionsExtension은SoftAssertionsProvider의 구현체를 모든 테스트 메서드의 파라미터로 주입하는 것을 지원합니다.
The term "test method" refers to any method annotated
with @Test, @RepeatedTest, @ParameterizedTest, @TestFactory or @TestTemplate.
Notably, the extension is compatible with parameterized tests,
the parameterized arguments must come first and the soft assertions argument last.
"test method"란,
@Test,@RepeatedTest,@ParameterizedTest,@TestFactory
또는@TestTemplate의 주석이 달린 모든 메서드를 말합니다.
특히, 확장은 parameterized test와 호환 가능하며, 파라미터가 먼저 오고 soft assertion 인수가 마지막에 와야합니다.
The scope of the SoftAssertionsProvider instance managed by this extension begins
when a parameter of type SoftAssertionsProvider is resolved for a test method.
It ends after the test method has been executed, this is when assertAll() will be invoked
on the instance to verify that no soft assertions failed.
이 확장에 의해 관리되는
SoftAssertionsProvider인스턴스의 범위는SoftAssertionsProvider유형의 파라미터가 테스트 메서드에 대해 확인될 때 시작됩니다.
테스트 메서드가 실행되고 난 이후에 종료됩니다. 이때 인스턴스에서assertAll()이 호출되어 soft assertion이 실패하지 않았는지 확인합니다.
Parameter injection and field injection can be mixed.
Assertions made on the field- and parameter-injected soft assertion providers
will all be collected and reported together when the extension calls assertAll().
파라미터 주입과 필드 주입은 섞어서 사용할 수 있습니다.
필드 및 파라미터가 주입된 soft assertion provider에 대한 검증은 확장이assertAll()을 호출할 때 모두 수집되어 함께 보고됩니다.
This extension throws a ParameterResolutionException if the resolved SoftAssertionsProvider :
이 확장은 아래의 경우
ParameterResolutionException을 발생시킵니다:
- abstract인 경우
- 기본 생성자가 없는 경우
Example:
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.api.extension.ExtendWith;
import org.assertj.core.api.BDDSoftAssertions;
import org.assertj.core.api.SoftAssertions;
import org.assertj.core.api.junit.jupiter.SoftAssertionsExtension;
@ExtendWith(SoftAssertionsExtension.class) // Junit5 확장 사용
public class JUnit5SoftAssertionsExample {
@Test
void junit5_soft_assertions_multiple_failures_example(SoftAssertions softly) { // SoftAssertions 사용 예시
softly.assertThat("George Martin").as("great authors").isEqualTo("JRR Tolkien");
softly.assertThat(42).as("response to Everything").isGreaterThan(100);
softly.assertThat("Gandalf").isEqualTo("Sauron");
// No need to call softly.assertAll(), this is automatically done by the SoftAssertionsExtension
// assertAll()을 호출할 필요가 없습니다. SoftAssertionsExtension에서 자동적으로 처리합니다.
}
@Test
void junit5_bdd_soft_assertions_multiple_failures_example(BDDSoftAssertions softly) { // BDDSoftAssertions 사용 예시
softly.then("George Martin").as("great authors").isEqualTo("JRR Tolkien");
softly.then(42).as("response to Everything").isGreaterThan(100);
softly.then("Gandalf").isEqualTo("Sauron");
// No need to call softly.assertAll(), this is automatically done by the SoftAssertionsExtension
// assertAll()을 호출할 필요가 없습니다. SoftAssertionsExtension에서 자동적으로 처리합니다.
}
@ParameterizedTest
@CsvSource({ "1, 1, 2", "1, 2, 3" })
// test parameters come first, soft assertion must come last.
// 테스트 파라미터가 먼저오고 soft assertion이 마지막에 와야 합니다.
void junit5_soft_assertions_parameterized_test_example(int a, int b, int sum, SoftAssertions softly) { // 파라미터 순서에 주의
softly.assertThat(a + b).as("sum").isEqualTo(sum);
softly.assertThat(a).isLessThan(sum);
softly.assertThat(b).isLessThan(sum);
}
}
As AutoCloseableSoftAssertions implements AutoCloseable#close() by calling assertAll(),
when used in a try-with-resources block assertAll() is called automatically before exiting the block.
AutoCloseableSoftAssertions는assertAll()을 호출하여AutoCloseable#close()을 구현하므로,
try-with-resources 블럭에서 사용될 때, 블럭이 종료되기 전에assertAll()이 자동으로 호출됩니다.
Example:
@Test
void auto_closeable_soft_assertions_example() {
try (AutoCloseableSoftAssertions softly = new AutoCloseableSoftAssertions()) { // resource 작성
softly.assertThat("George Martin").as("great authors").isEqualTo("JRR Tolkien");
softly.assertThat(42).as("response to Everything").isGreaterThan(100);
softly.assertThat("Gandalf").isEqualTo("Sauron");
// no need to call assertAll, this is done when softly is closed.
// 블럭이 종료되기 전에 assertAll이 자동으로 호출됩니다.
}
}
In a similar way you can use AutoCloseableBDDSoftAssertions where assertThat is replaced by then:
비슷한 방법으로
AutoCloseableBDDSoftAssertions을 사용하여assertThat대신then을 사용할 수 있습니다:
@Test
void auto_closeable_bdd_soft_assertions_example() {
try (AutoCloseableBDDSoftAssertions softly = new AutoCloseableBDDSoftAssertions()) { // resource 작성
softly.then("George Martin").as("great authors").isEqualTo("JRR Tolkien");
softly.then(42).as("response to Everything").isGreaterThan(100);
softly.then("Gandalf").isEqualTo("Sauron");
// no need to call assertAll, this is done when softly is closed.
// 블럭이 종료되기 전에 assertAll이 자동으로 호출됩니다.
}
}
The assertSoftly static method takes care of calling assertAll() before exiting.
assertSoftly정적 메서드는 종료 전assertAll()을 호출합니다.
Example:
@Test
void assertSoftly_example() {
SoftAssertions.assertSoftly(softly -> {
softly.assertThat("George Martin").as("great authors").isEqualTo("JRR Tolkien");
softly.assertThat(42).as("response to Everything").isGreaterThan(100);
softly.assertThat("Gandalf").isEqualTo("Sauron");
// no need to call assertAll(), assertSoftly does it for us.
// assertSoftly 정적 메서드를 사용하면 assertAll()을 명시적으로 호출할 필요가 없습니다.
});
}
Since the 3.16.0 version AssertJ provides a way to combine standard soft assertions
with custom ones in a single entry point.
AssertJ 3.16.0 버전 이후부터 단일 진입점에서 표준 soft assertion과 사용자 정의 assertion을 결합하는 방법을 제공합니다.
Let’s assume we have written an entry point for TolkienCharacter soft assertions
so that we can write assertions like:
아래와 같이 작성하기 위해
TolkienCharacter의 soft assertion 대한 진입점을 작성했다고 가정해봅시다:
TolkienSoftAssertions softly = new TolkienSoftAssertions();
softly.assertThat(frodo).hasRace(HOBBIT)
.hasName("Frodo");
If we want to check standard soft assertions we could make TolkienSoftAssertions inherit SoftAssertions
but if we want to have GoTSoftAssertions too then we are stuck as Java does not allow multiple inheritance.
만약 우리가 표준 soft assertion을 확인하길 원한다면 우리는
TolkienSoftAssertions가SoftAssertions을 상속하도록 만들 수 있을 것입니다.
그러나 Java는 다중 상속을 허용하지 않기 때문에GoTSoftAssertions도 상속받도록 만드는 것은 불가합니다.
The 3.16.0 release introduced the SoftAssertionsProvider interface to define soft assertions entry points.
3.16.0 릴리즈는 soft assertion 진입점을 정의하기 위한
SoftAssertionsProvider인터페이스를 도입했습니다.
Step 1
The first step consists in extending this interface to expose as many custom entry points as you need.
The typical custom SoftAssertionsProvider interface exposes default assertThat methods, as shown below:
첫 번째 단계는 이 인터페이스를 확장하여 필요한 만큼의 사용자 정의 entry point를 만드는 것입니다.
일반적인 사용자 정의SoftAssertionsProvider인터페이스는 아래와 같이assertThat메서드를 노출합니다.
public interface TolkienSoftAssertionsProvider extends SoftAssertionsProvider {
// custom assertions
// 사용자 정의 검증문
default TolkienCharacterAssert assertThat(TolkienCharacter actual) {
return proxy(TolkienCharacterAssert.class, TolkienCharacter.class, actual);
}
}
// let's add a Game of Thrones entry point
// Game of Thrones entry point를 추가해 보겠습니다
public interface GoTSoftAssertionsProvider extends SoftAssertionsProvider {
// custom assertions
// 사용자 정의 검증문
default GoTCharacterAssert assertThat(GoTCharacter actual) {
return proxy(GoTCharacterAssert.class, GoTCharacter.class, actual);
}
}
Step 2
In order to get a concrete entry point exposing all custom entry points,
create a class implementing all custom SoftAssertionsProvider and extending AbstractSoftAssertions.
AbstractSoftAssertions provides the core internal implementation to collect all errors
from the different implemented entry points (it also implements SoftAssertionsProvider).
모든 사용자 정의 entry points를 사용하는 구체적인 진입점을 얻으려면
모든 사용자 정의SoftAssertionsProvider를 구현하는 클래스를 만들고AbstractSoftAssertions를 상속받습니다.
AbstractSoftAssertions는 구현된 여러 entry point를 통해 모든 오류를 수집하는 핵심 내부 구현을 제공합니다. (SoftAssertionsProvider도 구현합니다)
To get standard soft assertions, inherit from SoftAssertions instead of AbstractSoftAssertions
(or BddSoftAssertions to get the BDD flavor).
표준 soft assertion을 얻으려면 AbstractSoftAssertions 대신 SoftAssertions을 상속받습니다. (BDD 특성을 얻으려면 BddSoftAssertions을 상속받습니다)
Let’s define our concrete entry points implementing both
TolkienSoftAssertionsProvider and GoTSoftAssertionsProvider:
TolkienSoftAssertionsProvider와GoTSoftAssertionsProvider를 모두 구현하는 구체적인 entry point를 정의해 보겠습니다:
// we extend SoftAssertions to get standard soft assertions
// SoftAssertion을 확장하여 표준 soft assertion을 얻습니다
public class FantasySoftAssertions extends SoftAssertions
implements TolkienSoftAssertionsProvider, GoTSoftAssertionsProvider {
// we can even add more assertions here
// 우리는 더 많은 검증문을 추가할 수도 있습니다
public HumanAssert assertThat(Human actual) {
return proxy(HumanAssert.class, Human.class, actual);
}
}
Step 3
The last step is to use FantasySoftAssertions:
마지막 단계는 FantasySoftAssertion을 사용하는 것입니다:
FantasySoftAssertions softly = new FantasySoftAssertions();
// custom TolkienCharacter assertions
// 사용자 정의 TolkienCharacter
softly.assertThat(frodo).hasRace(HOBBIT);
// custom GoTCharacter assertions
// 사용자 정의 GoTCharacter
softly.assertThat(nedStark).isDead();
// standard assertions
// 표준 assertions
softly.assertThat("Games of Thrones").startsWith("Games")
.endsWith("Thrones");
// verify assertions
// 검증문을 확인합니다
softly.assertAll();
사용자 정의 soft assertion(entry point)을 만드는 세 단계를 설명하고 있습니다.
각 단계는 AssertJ를 확장하여 원하는 만큼 많은 사용자 정의 entry point를 노출시키는 과정입니다.
첫 번째 단계는 인터페이스를 확장하여 필요한 만큼 많은 사용자 정의 진입점을 노출시키는 것입니다.
위 예시에서 TolkienSoftAssertionsProvider는 TolkienCharacter 객체에 대한 어서션을 위한 진입점을 노출시키고, GoTSoftAssertionsProvider는 GoTCharacter 객체에 대한 어서션을 위한 진입점을 노출시킵니다.
사용자는 필요에 따라 진입점을 추가하거나 수정할 수 있습니다.
두 번째 단계는 모든 사용자 정의 SoftAssertionsProvider를 구현하고 AbstractSoftAssertions를 확장하는 클래스를 만드는 것입니다.
FantasySoftAssertions은 TolkienSoftAssertionsProvider, GoTSoftAssertionsProvider를 implement하고 SoftAssertions을 extends 하고 있습니다.
Human 객체에 대한 어서션도 추가적으로 제공하고 있습니다.
마지막 단계는 FantasySoftAssertions를 사용하는 것입니다.
FantasySoftAssertions 인스턴스를 생성하고 사용자 정의 어서션 및 기본 어서션을 수행할 수 있습니다.
Optional step: use SoftAssertionsExtension
JUnit 5 SoftAssertionsExtension calls softly.assertAll() after each test
so that we don’t have to do it manually.
Since 3.16.0 it is capable of injecting any SoftAssertionsProvider,
we can then inject our custom FantasySoftAssertions:
JUnit5의
SoftAssertionsExtension은softly.assertAll()을 각 테스트 이후에 호출하기 때문에 우리가 명시적으로 호출하지 않아도 됩니다.
3.16.0 버전부터 모든SoftAssertionsProvider를 주입할 수 있으므로 사용자 지정FantasySoftAssertions또한 주입할 수 있습니다.
@ExtendWith(SoftAssertionsExtension.class) // 확장
public class JUnit5_StandardAndCustomSoftAssertionsExamples {
@Test
public void successful_junit_soft_custom_assertion_example(FantasySoftAssertions softly) { // 주입
softly.assertThat(frodo).hasName("Frodo")
.hasAge(33);
softly.assertThat(frodo.age).isEqualTo(33);
}
}
AssertJ allows to perform an action after an AssertionError is collected.
AssertJ는
AssertionError가 수집된 이후에 작업을 수행할 수 있습니다.
The action is specified by the AfterAssertionErrorCollected functional interface
which can be expressed as lambda, to register your callback call
setAfterAssertionErrorCollected as shown below:
아래 예시와 같이 작업은 콜백 호출
setAfterAssertionErrorCollected를 등록하는 람다를 표현할 수 있는AfterAssertionErrorCollected라는 함수형 인터페이스에 의해 지정됩니다.
Example:
SoftAssertions softly = new SoftAssertions();
StringBuilder reportBuilder = new StringBuilder(format("Assertions report:%n"));
// register our callback
// callback 등록
softly.setAfterAssertionErrorCollected(error -> reportBuilder.append(format("------------------%n%s%n", error.getMessage())));
// the AssertionError corresponding to the failing assertions are registered in the report
// 실패한 어설션에 해당하는 AssertionError가 보고서에 등록됩니다
softly.assertThat("The Beatles").isEqualTo("The Rolling Stones");
softly.assertThat(123).isEqualTo(123)
.isEqualTo(456);
resulting reportBuilder:
reportBuilder의 결과:
Assertions report:
------------------
Expecting:
<"The Beatles">
to be equal to:
<"The Rolling Stones">
but was not.
------------------
Expecting:
<123>
to be equal to:
<456>
but was not.
Alternatively, if you have defined your own SoftAssertions class and
inherited from AbstractSoftAssertions, you can instead override onAssertionErrorCollected(AssertionError).
또는 자체
SoftAssertions클래스를 정의하고AbstractSoftAssertions을 상속한 경우, onAssertionErrorCollected(AssertionError)를 재정의할 수 있습니다.
Example:
class TolkienSoftAssertions extends AbstractSoftAssertions {
public TolkienHeroesAssert assertThat(TolkienHero actual) {
return proxy(TolkienHeroesAssert.class, TolkienHero.class, actual);
}
@Override
public void onAssertionErrorCollected(AssertionError assertionError) {
System.out.println(assertionError);
}
}
TolkienSoftAssertions softly = new TolkienSoftAssertions();
TolkienCharacter frodo = TolkienCharacter.of("Frodo", 33, HOBBIT);
// the AssertionError corresponding to this failing assertion is printed to the console.
// 실패한 검증에 해당하는 AssertionError가 콘솔에 출력됩니다
softly.assertThat(frodo).hasName("Bilbo");
Assumptions provide support for conditional test execution,
if the assumptions are met the test is executed normally,
if they don’t the test is aborted and marked as ignored.
Assumptions은 조건부 테스트 실행을 제공합니다.
만약 가정이 맞다면 테스트는 정상적으로 실행되고,
가정이 틀렸다면 테스트는 중단되고 무시됩니다.
Assumptions are typically used whenever it does not make sense to continue
execution of a given test method — a typical usage is running tests depending on a given OS/environment.
Assumptions은 일반적으로 주어진 테스트를 계속 실행하기에 타당하지 않을 때 사용합니다.
일반적으로 주어진 운영체제나 환경에 따라 테스트를 실행할 때 사용합니다.
All AssertJ assumptions are static methods in the Assumptions class,
they match the assertion API but are names assumeThat instead of assertThat.
You can also get assumptions through the WithAssumptions interface.
모든 AssertJ assumption은
Assumptions클래스의 정적 메서드로,
assertion API와 일치하지만 이름은assertThat대신assumeThat을 사용합니다.
WithAssumptions인터페이스를 통해 가정문을 얻을 수도 있습니다.
Example resulting in the test to be ignored:
테스트가 무시되는 예:
@Test
public void when_an_assumption_is_not_met_the_test_is_ignored() {
// since this assumption is obviously false ...
// 이 가정은 틀렸기 때문에
assumeThat(frodo.getRace()).isEqualTo(ORC);
// ... this assertion is not performed
// 이후의 assertThat은 수행되지 않습니다
assertThat(fellowshipOfTheRing).contains(sauron);
}
Example resulting in the test to be executed normally:
정상적으로 테스트가 진행되는 예:
@Test
public void when_all_assumptions_are_met_the_test_is_run_normally() {
// since this assumption is true ...
// 이 가정은 옳기 때문에
assumeThat(frodo.getRace()).isEqualTo(HOBBIT);
// ... this assertion is performed
// assertion이 정상적으로 수행됩니다
assertThat(fellowshipOfTheRing).doesNotContain(sauron);
}
Assertions can be extended by using conditions, to create a condition you just have
to implement org.assertj.assertions.core.Condition and its unique method matches.
검증문은 condition을 통해 확장할 수 있습니다.
condition을 생성하려면org.assertj.assertions.core.Condition인터페이스를 구현하고
해당 인터페이스의 matches 메서드를 구현해야 합니다.
Once your Condition is created, you can use it with methods :
Condition을 생성하면 메서드와 함께 사용할 수 있습니다:
is(myCondition) or has(myCondition), both verifying that the condition is met
(hint: pick the one that makes your code more readable).
is(myCondition)또는has(myCondition), 둘 다 조건에 충족되는지 확인합니다.(코드를 더 읽기 쉽게 만드는 메서드를 선택하세요)
Example:
// jedi is a Condition
// jedi는 Condition입니다
assertThat("Yoda").is(jedi);
Conditions can be combined with :
Condition은 결합할 수 있습니다:
not(Condition): given condition must not be met
allOf(Condition...): all given conditions must be met
anyOf(Condition...): one of the given conditions must be met
not(Condition): 주어진 조건을 충족하지 않아야 합니다allOf(Condition...): 주어진 조건을 모두 충족해야 합니다anyOf(Condition...): 주어진 조건 중 하나는 충족해야 합니다
You can verify that a Condition is met on elements of a collection, with the following methods:
다음 방법을 사용하여 컬렉션의 요소들이 조건을 충족하는지 확인할 수 있습니다:
are(condition)/have(condition): all elements must meet the given condition
areAtLeast(n, condition)/haveAtLeast(n, condition): at least n elements must meet the given condition
areAtMost(n, condition)/haveAtMost(n, condition): no more than n elements must meet the given condition
areExactly(n, condition)/haveExactly(n, condition): exactly n elements must meet the given condition
are(condition)/have(condition): 모든 요소들이 다음의 조건을 충족해야 합니다areAtLeast(n, condition)/haveAtLeast(n, condition): 적어도 n개의 요소는 다음의 조건을 충족해야 합니다areAtMost(n, condition)/haveAtMost(n, condition): n개 이하의 요소들이 다음의 조건을 충족해야 합니다areExactly(n, condition)/haveExactly(n, condition): 정확히 n개의 요소들이 다음의 조건을 충족해야 합니다
Moreover all Condition related methods have their negation counterpart,
is/isNot, have/doesNotHave, are/areNot, ...
또한 모든 Condition 관련 메서드에는
is/isNot,have/doesNotHave,are/areNot와 같이 대응되는 부정 검증문이 있습니다
The examples below are from assertj-examples and more specifically UsingConditionExamples.java.
Let’s define two similar conditions: jedi and jediPower with the same implementation
to show that code readability is better when using jedi with is and jediPower with has.
jedi는is와 함께 사용하고jediPower는has와 함께 사용할 때 코드 가독성이 더 좋다는 것을 보여주기 위해
두 가지 유사한 조건을 정의해 보겠습니다:
import static org.assertj.core.util.Sets.newLinkedHashSet;
static Set<String> JEDIS = newLinkedHashSet("Luke", "Yoda", "Obiwan");
// implementation with Java 8 lambda
// lambda를 사용하여 구현
Condition<String> jediPower = new Condition<>(JEDIS::contains, "jedi power");
// implementation with Java 7
// lambda를 사용하지 않고 Condition을 구현한 다음 matches 메서드를 구현
Condition<String> jedi = new Condition<String>("jedi") {
@Override
public boolean matches(String value) {
return JEDIS.contains(value);
}
};
The String parameter is describing the condition, this is used in error messages.
String 매개변수는 조건을 설명하며 오류 메시지에 사용됩니다.
Usage with single instances:
단일 인스턴스 사용:
assertThat("Yoda").is(jedi);
assertThat("Vader").isNot(jedi);
assertThat("Yoda").has(jediPower);
assertThat("Solo").doesNotHave(jediPower);
The Condition description is used in error messages, ex:
조건 설명은 다음과 같은 오류 메시지에 사용됩니다:
assertThat("Vader").is(jedi);
will fail with the following error message:
다음 오류 메시지와 함께 실패합니다:
"expecting:<'Vader'> to be:<jedi>"
Usage with collections:
컬렉션에서의 사용:
assertThat(list("Luke", "Yoda")).are(jedi);
assertThat(list("Leia", "Solo")).areNot(jedi);
assertThat(list("Luke", "Yoda")).have(jediPower);
assertThat(list("Leia", "Solo")).doNotHave(jediPower);
assertThat(list("Luke", "Yoda", "Leia")).areAtLeast(2, jedi);
assertThat(list("Luke", "Yoda", "Leia")).haveAtLeast(2, jediPower);
assertThat(list("Luke", "Yoda", "Leia")).areAtMost(2, jedi);
assertThat(list("Luke", "Yoda", "Leia")).haveAtMost(2, jediPower);
assertThat(list("Luke", "Yoda", "Leia")).areExactly(2, jedi);
assertThat(list("Luke", "Yoda", "Leia")).haveExactly(2, jediPower);
Conditions can be combined with allOf(Condition...) (logical and) or anyOf(Condition...) (logical or),
not can be used to invert one.
조건은
allOf(Condition...)(논리적 and) 또는anyOf(Condition...)(논리적 or) 와 결합될 수 있으며,
하나를 뒤집는 데not을 사용할 수 있습니다.
Let’s define a sith condition:
sith 조건을 정의해보겠습니다:
List<String> SITHS = list("Sidious", "Vader", "Plagueis");
Condition<String> sith = new Condition<>(SITHS::contains, "sith");
We can write these assertions:
다음과 같은 검증문을 작성할 수 있습니다:
assertThat("Vader").is(anyOf(jedi, sith));
assertThat("Solo").is(allOf(not(jedi), not(sith)));
This page will help you converting your existing JUnit assertions to AssertJ ones. Note that both types of assertions can coexist, you don’t have to migrate all at once.
이번 장은 기존의 JUnit 검증문을 AssertJ 검증문으로 변환하는 법에 대해 설명합니다. 두 유형의 검증문은 공존할 수 있으므로 한 번에 모두 마이그레이션할 필요는 없습니다.
The idea is to convert code like :
다음과 같은 코드를:
assertEquals(expected, actual); // Junit Assertion
to:
아래와 같이 변경하는 것입니다:
assertThat(actual).isEqualTo(expected); // AssertJ Assertion
It is as simple as running one of the following script depending on which test framework you are using:
사용 중인 테스트 프레임워크에 따라 다음 스크립트 중 하나를 실행하기만 하면 됩니다.
JUnit 3/4 to AssertJ migration script : JUnit 3/4용
JUnit 5 to AssertJ migration script : JUnit 5용
TestNG to AssertJ migration script : TestNG용
Each shell scripts is based on the sed stream editor and regexps. It recursively looks at all *Test.java files and performs search and replace to convert assertions to AssertJ ones.
각 쉘 스크립트는 sed 스트림 에디터와 정규식을 기반으로 합니다.
이 스크립트는 재귀적으로 모든*Test.java파일을 살펴보고 검색과 치환 작업을 수행하여 assertion들을 AssertJ assertions로 변환합니다.
The script handles the cases where you use an assertion description, for example:
스크립트는 다음과 같이 assertion 설명을 사용하는 경우도 처리합니다:
assertEquals("test context", "a", "a");
will be replaced by:
위와 같이 작성된 assertion은 다음과 같이 변환됩니다:
assertThat("a").as("test context").isEqualTo("a");
Note that the script does a best effort and some assertions might not be converted if formatted on multiple lines. Anyway the script usually migrates the vast majority of assertions.
스크립트가 최선을 다하겠지만 일부 변환이 제대로 이루어지지 않을 수 있습니다.
그래도 대부분의 검증문을 성공적으로 마이그레이션합니다.
The script works on windows within a bash console like git bash (tested a long time ago) or cygwin (not tested).
스크립트는 windows의 git bash나(예전 버전에서 테스트됨) cygwin(테스트하지 않음)과 같은 bash 콘솔에서도 작동할 것입니다.
Execute the script in the base directory containing the test files:
테스트 파일을 포함하는 베이스 디렉토리에서 스트립트를 실행하세요:
cd ./src/test/java
./convert-junit-assertions-to-assertj.sh
If the *Test.java file pattern does not suit you, just specify another as an argument:
만약
*Test.java파일 패턴이 적합하지 않으면 다른 것을 인수로 지정하세요:
# enclose your pattern with double quotes "" to avoid it to be expanded by your shell prematurely
./convert-junit-assertions-to-assertj.sh "*IT.java"
After executing it, you will need to :
실행한 뒤에 다음과 같은 것들을 해야합니다:
- 사용하지 않는 import문들을 제거하고 최적화하세요
assertEquals with a delta to compare numbers, you will need to statically import org.assertj.core.api.Assertions.within which is how you express deltas in AssertJ (see the number_assertions_with_offset_examples() test at the end of NumberAssertionsExamples).
- 만약 delta를 사용하여 숫자를 비교할 때 assertEquals를 사용한다면, AssertJ에서 delta를 표현하는 방법인 org.assertj.core.api.Assertions.within을 정적으로 가져와야 합니다. (NumberAssertionsExamples의 끝에 있는 number_assertions_with_offset_examples() 테스트를 참고하세요.)
여기서 "delta"는 숫자 비교에서 허용 가능한 오차 범위를 의미합니다. 예를 들어 두 실수 값 A와 B를 비교할 때, A와 B의 차이가 특정한 오차 범위 내에 있는지 확인하고 싶다면, delta를 사용하여 두 값의 차이가 허용 가능한 오차 범위 내에 있는지 검사할 수 있습니다.
스크립트 결과 출력 예시
Converting JUnit assertions to AssertJ assertions on files matching pattern : *Test.java
1 - Replacing : assertEquals(0, myList.size()) ............... by : assertThat(myList).isEmpty()
2 - Replacing : assertEquals(expectedSize, myList.size()) .... by : assertThat(myList).hasSize(expectedSize)
3 - Replacing : assertEquals(expectedDouble, actual, delta) .. by : assertThat(actual).isCloseTo(expectedDouble, within(delta))
4 - Replacing : assertEquals(expected, actual) ............... by : assertThat(actual).isEqualTo(expected)
5 - Replacing : assertArrayEquals(expectedArray, actual) ..... by : assertThat(actual).isEqualTo(expectedArray)
6 - Replacing : assertNull(actual) ........................... by : assertThat(actual).isNull()
7 - Replacing : assertNotNull(actual) ........................ by : assertThat(actual).isNotNull()
8 - Replacing : assertTrue(logicalCondition) ................. by : assertThat(logicalCondition).isTrue()
9 - Replacing : assertFalse(logicalCondition) ................ by : assertThat(logicalCondition).isFalse()
10 - Replacing : assertSame(expected, actual) ................. by : assertThat(actual).isSameAs(expected)
11 - Replacing : assertNotSame(expected, actual) .............. by : assertThat(actual).isNotSameAs(expected)
Replacing JUnit static imports by AssertJ ones, at this point you will probably need to :
12 --- optimize imports with your IDE to remove unused imports
12 --- add "import static org.assertj.core.api.Assertions.within;" if you were using JUnit number assertions with deltas