목차
JUnit4 Assert, Assume
Assert, Assume 의 경우 JUnit 5에서 아래와 같이 대체가 된 것으로 확인된다.
JUnit4 | JUnit5 |
org.junit.Assert | org.junit.jupiter.api.Assertions |
org.junit.Assume | org.junit.jupiter.api.Assumptions |
개인적으로 버전업에 따른 API 추가 외에 보이는 차이점은 아래와 같다.
- JUnit4 Assert 에서는 Hamcrest Matcher 파라미터로 지원하였으나, JUnit5에서는 지원하지 않음
- 마찬가지로 JUnit4 Assume에서도 Hamcrest Matcher 지원하였으나, JUnit5에서는 지원하지 않고 함수형 인터페이스(BooleanSupplier) 지원하면서, 람다식 인자 전달 가능해짐
*JUnit5 API를 먼저 살펴보고 JUnit4 API가 궁금할 경우 아래 Doc 참고
JUnit5 Assertions
패키지 org.junit.jupiter.api.Assertions
정의 Assertions is a collection of utility methods that support asserting conditions in tests.
- 아래의 경우 공식 문서의 예제 코드를 사용함
- 자세한 API 명세의 경우 JUnit5 Assertions API Doc 참고
- spring-boot-start-test 의존성 추가하면 테스트 라이브러리 포함되어 있음 (spring boot 2.7.0, maven 참고)
dependencies {
//..
testImplementation 'org.springframework.boot:spring-boot-starter-test'
//..
}
1) 기본 - assertEquals, assertNotEquals
두 값이 일치하는지 확인
@Test
void standardAssertions() {
double result = Calculator.PLUS.apply(1, 2);
assertEquals(3, result); // ok
}
두 값이 다른지 확인 (같을 경우 메시지 출력)
@Test
void assertNotEquals() {
int value = 0;
// AssertionFailedError: the result cannot be 0
Assertions.assertNotEquals(0, value, "the result cannot be 0");
}
2) 그룹 - assertAll
- 그룹화된 Assertion 테스트를 실행
- Executable 인자로 람다식 사용하여 여러개의 테스트 실행
- 실행 후 실패 건에 대해 MultipleFailuresError 생성자에 heading 정보와 함께 전달하여 콘솔 출력
// Assertions.class
public static void assertAll(Executable... executables) throws MultipleFailuresError {
AssertAll.assertAll(executables);
}
public static void assertAll(String heading, Executable... executables) throws MultipleFailuresError {
AssertAll.assertAll(heading, executables);
}
- Executable 의 경우 @FunctionalInterface 이기 때문에 람다식으로 표현 가능
@FunctionalInterface
@API(status = STABLE, since = "5.0")
public interface Executable {
void execute() throws Throwable;
}
예시
private final Person person = new Person("Jane", "Doe");
@Test
void groupedAssertions() {
assertAll("person",
() -> assertEquals("Jane", person.getFirstName()),
() -> assertEquals("Doe", person.getLastName())
);
}
@Test
void dependentAssertions() {
// Within a code block, if an assertion fails the
// subsequent code in the same block will be skipped.
assertAll("properties",
() -> {
String firstName = person.getFirstName();
assertNotNull(firstName);
// Executed only if the previous assertion is valid.
assertAll("first name",
() -> assertTrue(firstName.startsWith("J")),
() -> assertTrue(firstName.endsWith("e"))
);
},
() -> {
// Grouped assertion, so processed independently
// of results of first name assertions.
String lastName = person.getLastName();
assertNotNull(lastName);
// Executed only if the previous assertion is valid.
assertAll("last name",
() -> assertTrue(lastName.startsWith("D")),
() -> assertTrue(lastName.endsWith("e"))
);
}
);
}
3) 예외 확인 - assertThrows
- 메서드 실행 했을 때 특정 예외가 발생하였는지 확인
- 첫번째 인자(expectedType) : 확인할 예외 클래스 타입 토큰
- 두번째 인자(executable) : 실행하려는 메서드(람다식)
// Assertions.class
public static <T extends Throwable> T assertThrows(Class<T> expectedType, Executable executable) {
return AssertThrows.assertThrows(expectedType, executable);
}
예시
@Test
void exceptionTesting() {
Exception exception = assertThrows(ArithmeticException.class,
() -> Calculator.DIVIDE.apply(1, 0));
assertEquals("/ by zero", exception.getMessage());
}
4) 시간 초과 - assertTimeout, assertTimeoutPreemptively
- assertTimeout : 특정시간 안에 테스트가 끝나는지 확인, 시간 초과 하더라도 실행 메서드가 끝난 후 결과 리턴
- assertTimeoutPreemptively : 특정시간 안에 테스트가 끝나지 않을 경우 바로 강제 종료
@Test
void timeoutExceeded() {
// The following assertion fails with an error message similar to:
// execution exceeded timeout of 10 ms by 91 ms
assertTimeout(ofMillis(10), () -> {
// Simulate task that takes more than 10 ms.
Thread.sleep(100);
});
}
@Test
void timeoutExceededWithPreemptiveTermination() {
// The following assertion fails with an error message similar to:
// execution timed out after 10 ms
assertTimeoutPreemptively(ofMillis(10), () -> {
// Simulate task that takes more than 10 ms.
new CountDownLatch(1).await();
});
}
시간 제한을 10ms 두고, 100ms 씩 Thread를 정지 시켰을 때 총 127ms 실행 시간이 소요되는 것을 확인가능했다
총 127ms = 100ms (assertTimeout) + 10ms(assertTimeoutPreemptively) + @ (자원해제)
@Test
void timeoutExceedTest() {
assertAll(
() -> assertTimeout(Duration.ofMillis(10), () -> Thread.sleep(100)),
() -> assertTimeoutPreemptively(Duration.ofMillis(10), () -> Thread.sleep(100))
);
}
Baeldung 예시 참고
5) assertArrayEquals
- 예상 배열과 실제 배열이 동일한지 확인
- 같지 않을 경우 세번째 인자 메시지 출력
@DisplayName("배열의 값이 같은지 확인한다")
@Test
void assertArrayEquals() {
char[] expected = {'J', 'u', 'p', 'i', 't', 'e', 'r'};
char[] actual = "Jupiter".toCharArray();
Assertions.assertArrayEquals(expected, actual, "Arrays should be equal");
}
6) assertTrue , assertFalse
- assertTrue의 경우 조건이 true이면 테스트 통과, false이면 메시지 출력
- assertFalse의 경우 조건이 false이면 테스트 통과, true인 경우 메시지 출력
@DisplayName("조건이 참인가")
@Test
void assertTrue() {
Assertions.assertTrue(5 > 4, "5 is greater than 4");
Assertions.assertTrue(null == null, "null is equal to null");
BooleanSupplier condition = () -> 10 > 1;
Assertions.assertTrue(condition);
}
@DisplayName("조건이 거짓인가")
@Test
void assertFalse() {
BooleanSupplier condition = () -> 5 > 6;
Assertions.assertFalse(condition, "5 is not greater than 6");
}
7) assertNull, assertNotNull
- 객체의 null 여부 확인
- assertNull : 객체가 null 이면 true
- assertNotNull : 객체가 not null 이면 true
@DisplayName("")
@Test
void assertNotNull() {
Object dog = new Object();
Assertions.assertNotNull(dog, () -> "The dog should not be null");
}
@DisplayName("")
@Test
void assertNull() {
Object cat = null;
Assertions.assertNull(cat, () -> "The cat should be null");
}
8) assertSame, assertNotSame
동일한 Object 참조 주소를 가지는지 판별
@DisplayName("")
@Test
void assertSame() {
String language = "Java";
Optional<String> optional = Optional.of(language);
Assertions.assertSame(language, optional.get()); // ok
}
9) assertIterableEquals
- Iterable 인터페이스를 구현한 클래스에 대해 deeply equals(내용, 값) 비교
- 동일한 순서로 내용만 일치하는지 판별
- 아래와 같이 타입은 달라도 상관없음
@Test
void assertIterableEquals() {
Iterable<String> al = new ArrayList<>(Arrays.asList("Java", "Junit", "Test"));
Iterable<String> ll = new LinkedList<>(Arrays.asList("Java", "Junit", "Test"));
Assertions.assertIterableEquals(al, ll);
}
10) assertLinesMatch
- 예상과 결과 List 인스턴스가 같은지 확인
- String.macthes() 로 인덱스 별로 두 인자가 동일한지 판별 (패턴 검사 수행)
- fast-forward marker 확인(이해 못함)
@Test
void assertLinesMatch() {
List<String> expected = Arrays.asList("Java", "\\d+", "JUnit");
List<String> actual = Arrays.asList("Java", "11", "JUnit");
Assertions.assertLinesMatch(expected, actual);
}
두번째 "\\d+" 의 경우 digit 숫자에 + (하나 이상) 을 뜻하는 정규 표현식이라서 "11"과 비교시 통과
11) fail
- 해당 테스트는 fail로 끝나고 실패 메시지를 콘솔 출력
- 개발이 완료 되지 않은 테스트를 표시하는 경우 유용
@Test
void fail() {
// Test not completed
Assertions.fail("FAIL - test not completed");
}
JUnit5 Assumptions
패키지 org.junit.jupiter.api.Assumptions
정의 Assumptions is a collection of utility methods that support conditional test execution based on assumptions.
- Assumption : 추정, 가정
공식 문서 예제 코드
Assumptions API 의 경우 특정 환경인 경우를 가정하여 테스트 진행하도록 합니다
@DisplayName("true인 경우 테스트 통과, false 인 경우 test ignore 된다")
@Test
void testOnlyOnCiServer() {
assumeTrue("CI".equals(System.getenv("ENV")));
}
@DisplayName("true인 경우 테스트 통과, false 인 경우 test ignore 된다")
@Test
void testOnlyOnDeveloperWorkstation() {
assumeTrue("DEV".equals(System.getenv("ENV")),
() -> "Aborting test: not on developer workstation");
}
@DisplayName("true인 경우 Executable 실행하고, false 인 경우 해당 테스트를 건너 다음 테스트를 실행한다")
@Test
void testInAllEnvironments() {
assumingThat("CI".equals(System.getenv("ENV")),
() -> {
// perform these assertions only on the CI server
assertEquals(2, Calculator.DIVIDE.apply(4, 2));
});
assertEquals(42, Calculator.MULTIPLY.apply(6, 7));
}
- 환경 변수(ENV=CI) 설정 후 테스트 실행시 두번째 테스트만 Ignore 되고, 나머지는 통과한다
참고
Assertions in JUnit4 and JUnit5
https://junit.org/junit5/docs/current/user-guide/#writing-tests-assertions
https://junit.org/junit5/docs/current/user-guide/#writing-tests-assumptions
'공부 > Junit' 카테고리의 다른 글
[JUnit5] 테스트 메서드 그룹화/실행 순서 (@Nested, @TestMethodOrder) (0) | 2023.08.26 |
---|---|
[JUnit] JUnit4, JUnit5 어노테이션 비교 (생명주기, ParameterizedTest, Suite 등등) (0) | 2023.08.11 |
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!