JUnit5 검증 - Assertion
Assertion
JUnit5는 Assertions 라는 단언문 클래스를 제공한다.
Assertion(단언문)은 제공되는 메소드를 사용해서 테스트에서 검증하고자하는 값이나 동작을 확인하는 기능을 한다.
값 검증 Assertion
기본적인 단언문 메소드가 준비되어 있다.
assertEquals
assertEquals(expect, actual, {message})
는 실제 값(actual)이 기대한 값(expect)과 같은지 확인한다.
package com.devkuma.junit5.assertion;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;
public class AssertEqualsTest {
@Test
void basicTest() {
assertEquals(1, 2);
}
@Test
void numberTest() {
assertEquals(1, 2, "숫자 비교");
}
@Test
void stringTest() {
assertEquals("abc", "ABC", "문자 비교");
}
}
실행 결과:
expected: <1> but was: <2>
Expected :1
Actual :2
숫자 비교 ==> expected: <1> but was: <2>
Expected :1
Actual :2
문자 비교 ==> expected: <abc> but was: <ABC>
Expected :abc
Actual :ABC
assertNull(actual)
assertNull(actual, {message})
는 값이 null
인지 확인한다.
package com.devkuma.junit5.assertion;
import static org.junit.jupiter.api.Assertions.assertNull;
import org.junit.jupiter.api.Test;
public class AssertNullTest {
@Test
void fail() {
String name = null;
assertNull(name, "name is null");
}
@Test
void success() {
String name = "kimkc";
assertNull(name, "name is null");
}
}
assertNotNull(actual)
assertNotNull(actual, {message})
는 값이 null이 아닌지 확인한다.
package com.devkuma.junit5.assertion;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import org.junit.jupiter.api.Test;
public class AssertNotNullTest {
@Test
void fail() {
String name = null;
assertNotNull(name, "name is not null");
}
@Test
void success() {
String name = "kimkc";
assertNotNull(name, "name is not null");
}
}
assertTrue(boolean),
assertTrue(actual, {message})
는 다음 조건이 참(true)인지 확인한다.
package com.devkuma.junit5.assertion;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.Test;
public class AssertTrueTest {
@Test
void trueTest() {
boolean a = true;
assertTrue(a);
}
@Test
void arithmeticTrue() {
int a = 3;
int b = 10;
assertTrue(a > b, "a가 b보다 커야 한다.");
}
}
assertFalse(boolean)
assertFalse(actual, {message})
는 다음 조건이 참(false)인지 확인한다.
package com.devkuma.junit5.assertion;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.Test;
public class AssertFalseTest {
@Test
void falseTest() {
boolean a = true;
assertFalse(a);
}
@Test
void arithmeticFalse() {
int a = 3;
int b = 10;
assertFalse(a > b, "a가 b보다 커지 말아야 한다.");
}
}
편리한 Assertion
값 검증을 위한 메소드 외에도, 편리한 메소드도 준비되어 있다.
assertAll(executable…)
assertAll(executable...)
은 모든 테스트를 한번에 실행할 수 있다.
아래와 같이 여러개의 단언문을 사용할때, 앞에 테스트가 실패하게 되면 다음 테스트는 실행되지 않는다. 그러기 때문에 다음 테스트가 실패했는 지 알 수 없다.
@Test
void basicTest() {
String name = null;
assertNotNull(name, "name is not null");
assertEquals(1, 2);
}
이럴때 assertAll
메소드를 사용하면, 전체 테스트의 결과를 전부 확인할 수 있다.
@Test
void allTest() {
final String name = null;
assertAll(
() -> assertNotNull(name, "name is not null"),
() -> assertEquals(1, 2)
);
}
전체 코드는 아래와 같다.
package com.devkuma.junit5.assertion;
import static org.junit.jupiter.api.Assertions.assertAll;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import org.junit.jupiter.api.Test;
public class AssertAllTest {
@Test
void basicTest() {
String name = null;
assertNotNull(name, "name is not null");
assertEquals(1, 2);
}
@Test
void allTest() {
final String name = null;
assertAll(
() -> assertNotNull(name, "name is not null"),
() -> assertEquals(1, 2)
);
}
}
assertThrows(expectedType, executable)
assertThrows
는 예외 발생하는지에 대해서 확인할 수 있다. 발생한 예외의 메세지가 동일한지도 할 수도 있다.
package com.devkuma.junit5.assertion;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
public class AssertThrowsTest {
void setNumber(int number) {
if (number < 0) {
throw new IllegalArgumentException("number must be greater than 0.");
}
}
@Test
void test() {
IllegalArgumentException exception = assertThrows(
IllegalArgumentException.class,
() -> setNumber(-5)
);
String message = exception.getMessage();
assertEquals("number must be greater than 0.", message);
}
}
assertTimeout(duration, executable)
assertTimeout(duration, executable)
은 특정 시간 안에 실행이 완료되는지 확인한다.
두번째 매개변수의 람다식이 종료되면 테스트가 같이 종료된다.
package com.devkuma.junit5.assertion;
import static org.junit.jupiter.api.Assertions.assertTimeout;
import java.time.Duration;
import org.junit.jupiter.api.Test;
public class AssertTimeoutTest {
@Test
void timeoutTest() {
assertTimeout(Duration.ofMillis(100), () -> {
Thread.sleep(300);
});
}
}
- 위 테스트 1초 안에 실행되는 것을 테스트를 하는 건데, 3초기 넘어 실패하게 된다.
assertTimeoutPreemptively(duration, executable)
assertTimeoutPreemptively
는 특정 시간 안에 실행이 완료되는지 확인한다.
assertTimeout
와의 차이점은 assertTimeout
는 지정한 시간까지 기다렸다고 종료되지만, assertTimeoutPreemptively
는 지정한 시간이 지나면 테스트가 바료 종료된다.
package com.devkuma.junit5.assertion;
import static org.junit.jupiter.api.Assertions.assertTimeoutPreemptively;
import java.time.Duration;
import org.junit.jupiter.api.Test;
public class AssertTimeoutPreemptivelyTest {
@Test
void timeoutTest() {
assertTimeoutPreemptively(Duration.ofMillis(100), () -> {
Thread.sleep(300);
});
}
}
주의점
두 번쨰 매개변수인 람다식 로직을 별도에Thread
에서 실행을 하기 때문에 주의해서 사용해야 한다.예를들어, Database에 트랜잭션 코드가 있다면 롤백이 안될 수 있다.
다른 Assertion 라이브러리
JUnit5는 모든 다른 단언문 라이브러리를 사용할 수도 있다. AssertJ, Hamcrest 등을 의존 라이브러리에 추가하면, JUnit4 때와 같이 어렵지 않게 사용할 수 있다.
AssertJ
메소드 체이닝을 지원하기 때문에 좀 더 깔끔하고 읽기 쉬운 테스트 코드를 작성할 수 있다.
AssertJ를 종속성 라이브러리는 따로 추가해야 한다.
build.gradle
dependencies {
// ...
testImplementation 'org.assertj:assertj-core:3.23.1'
}
AssertJ를 사용한 예제는 다음과 같다.
package com.devkuma.junit5.assertion;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
public class AssertjTest {
@Test
void numberTest() throws Exception {
assertThat(10) // 주어진 10 숫자는
.isZero() // 0이고,
.isNotZero() // 0이 아니고,
.isPositive() // 양수 이고,
.isEqualTo(8); // 8과 일치한다.
}
@Test
void stringTest() {
assertThat("Hello, world! Nice to meet you.") // 주어진 "Hello, world! Nice to meet you." 문자열은
.isNotEmpty() // 비어있지 않고
.contains("Nice") // "Nice"를 포함하고
.contains("world") // "world"도 포함하고
.doesNotContain("ZZZ") // "ZZZ"는 포함하지 않으며
.startsWith("Hell") // "Hell"로 시작하고
.endsWith("u.") // "u."로 끝나며
.isEqualTo("Hello, world! Nice to meet you."); // "Hello, world! Nice to meet you."과 일치한다.
}
}