오늘은 Junit5의 Assertion입니다.
이 글은 백기선님 더 자바, 애플리케이션을 테스트하는 다양한 방법을 정리한 글입니다.
더 자바, 애플리케이션을 테스트하는 다양한 방법
더 자바, 애플리케이션을 테스트하는 다양한 방법 - 인프런 | 강의
자바 프로그래밍 언어를 사용하고 있거나 공부하고 있는 학생 또는 개발자라면 반드시 알아야 하는 애플리케이션을 테스트하는 다양한 방법을 학습합니다., 그냥 개발자를 넘어 '더 나은 개발
www.inflearn.com
Assertion
Assertion는 Junit에서 제공하는 검증하고자 하는 내용을 확인하는 방법입니다.
Assertions 중요 메서드는 6가지가 있습니다.
하나하나 보면서 알아보도록 하겠습니다.
- assertNotNull
- assertEquals
- assertTrue
- assertAll
- assertThrows
- assertTimeout
assertNotNull
null 인지 아닌지 확인하는 데 사용되는 메서드입니다.
@Test
@DisplayName("assertNotNull 테스트")
void assertNotNullTest() {
Study study = new Study();
assertNotNull(study);
}
assertEquals
두 값이 같은지 확인하는 데 사용되는 메서드입니다.
@Test
@DisplayName("assertEquals 테스트")
void assertEqualsTest() {
Study study = new Study();
assertEquals(StudyStatus.DRAFT, study.studyStatus, "스터디를 처음 만들면 상태값이" + StudyStatus.DRAFT + "여야 한다.");
}
public static void assertEquals(Object expected, Object actual, String message) {
AssertEquals.assertEquals(expected, actual, message);
}
assertEquals는 expected, actual, message를 받고 첫 번째 인자가 기댓값, 두 번째 인자가 실제값을 받고 있어 둘이 바꿔 써도 상관은 없지만 메서드의 의도대로 기댓값은 expect에 실제값은 actual에 쓰는 것이 의도대로 쓰는 것이지 않을까 합니다.
assertTrue
주어진 조건이 참인지 확인하는 데 사용됩니다.
@Test
@DisplayName("assertTrue 테스트")
void assertTrueTest() {
Study study = new Study();
study.setLimit(5);
assertTrue(study.getLimit() > 10, () -> "스터디는 10보다 큽니까?");
}
assertAll
여러 개의 Assertions를 그룹화하여 모두 실행되도록 하는 메서드입니다.
@Test
@DisplayName("assertAll 테스트")
void assertAllTest() {
Study study = new Study();
study.setLimit(5);
assertEquals(StudyStatus.STARTED, study.studyStatus, "스터디를 처음 만들면 상태값이" + StudyStatus.DRAFT + "여야 한다.");
assertTrue(study.getLimit() > 10, () -> "스터디는 10보다 큽니까?");
}
이런 테스트 메서드를 만들어서 진행할 경우
만약 assertEquals에서 검증 실패한다면 assertTrue는 실행되지 않습니다.
이럴 경우 오류 찾고 해결하고 또 오류 찾고 해결하는 번거로움이 발생될 수 있습니다.
그래서 assertAll를 쓸 경우 검증이 실패해도 모든 검증을 진행할 수 있습니다.
@Test
@DisplayName("assertAll 테스트")
void assertAllTest() {
Study study = new Study();
study.setLimit(5);
assertAll(
() -> assertNotNull(study),
() -> assertEquals(StudyStatus.STARTED, study.studyStatus, "스터디를 처음 만들면 상태값이" + StudyStatus.DRAFT + "여야 한다."),
() -> assertTrue(study.getLimit() > 10, () -> "스터디는 10보다 큽니까?")
);
}
assertAll을 찾아보면
static void assertAll(Executable... executables) {
assertAll(null, executables);
}
static void assertAll(String heading, Executable... executables) {
Preconditions.notEmpty(executables, "executables array must not be null or empty");
Preconditions.containsNoNullElements(executables, "individual executables must not be null");
assertAll(heading, Arrays.stream(executables));
}
Executable...으로 Executable 파라미터를 여러 개 받는 형식입니다.
assertThrows
예외가 발생하는지 확인하는 메서드입니다.
첫 번째 인자로 Exception 클래스. 두 번째로는 Executable를 받습니다.
@Test
@DisplayName("assertThrows 테스트")
void assertThrowsTest() {
IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> new Study().setLimit(20));
String message = exception.getMessage();
assertEquals("스터디의 제한은 10입니다.", message);
}
assertTimeout
주어진 작업이 지정된 시간 내에 완료되는지 확인하는 데 사용되는 메서드입니다.
첫 번째 인자는 Duration, 두 번째 인자는 Executable입니다.
@Test
@DisplayName("assertTimeout 테스트")
void assertTimeoutTest() {
Study study = new Study();
assertTimeout(Duration.ofMillis(300), () -> {
study.getStudyStatus();
Thread.sleep(1000);
});
assertTimeoutPreemptively(Duration.ofMillis(300), () -> {
study.getStudyStatus();
Thread.sleep(1000);
});
}
assertTimeout를 사용할 경우 검증이 실패해도 모두 끝날 때까지 기다려야 합니다.
300밀리 세컨즈이지만 Thread.sleep(1000)가 끝나길 기다려야 합니다.
그래서 검증이 실패할 때 검증을 종료하고 싶으면 assertTimeoutPreemptively메서드를 사용하면 됩니다.
하지만 assertTimeoutPreemptively를 사용하려면 주의점이 있습니다.
assertTimeoutPreemptively Thread를 별도로 사용하기 때문에 ThreadLocal을 테스트할 경우 테스트가 제대로 진행되지 않을 수 있습니다. EX) 트랜젝션이 정상적으로 작동하지 않아 롤배되지 않고 DB저장 가능
static <T, E extends Throwable> T assertTimeoutPreemptively(Duration timeout, ThrowingSupplier<T> supplier,
Supplier<String> messageSupplier, Assertions.TimeoutFailureFactory<E> failureFactory) throws E {
AtomicReference<Thread> threadReference = new AtomicReference<>();
ExecutorService executorService = Executors.newSingleThreadExecutor(new TimeoutThreadFactory());
try {
Future<T> future = submitTask(supplier, threadReference, executorService);
return resolveFutureAndHandleException(future, timeout, messageSupplier, threadReference::get,
failureFactory);
}
finally {
executorService.shutdownNow();
}
}
assertTimeoutPreemptively을 보면
ExecutorService executorService = Executors.newSingleThreadExecutor(new TimeoutThreadFactory());로 Thread생성하는 걸 볼 수 있습니다.
공통적으로 모든 검증에는 message를 보낼 수 있는데 메시지는 String 또는 Supplier <String>으로 받을 수 있습니다.
String을 사용할 때 message안에서 연산이 일어날 경우 모든 메시지를 연산하게 됩니다.
Supplier <String>을 사용할 경우 message를 사용해야 할 때 사용하게 됩니다.
그래서 String을 사용할 경우 비용이 증가하게 됩니다.
@Test
@DisplayName("assertEquals 테스트")
void assertEqualsTest() {
Study study = new Study();
assertEquals(StudyStatus.DRAFT, study.studyStatus, "스터디를 처음 만들면 상태값이" + StudyStatus.DRAFT + "여야 한다.");
assertEquals(StudyStatus.DRAFT, study.studyStatus, "스터디를 처음 만들면 상태값이" + StudyStatus.DRAFT + "여야 한다.");
assertEquals(StudyStatus.DRAFT, study.studyStatus, "스터디를 처음 만들면 상태값이" + StudyStatus.DRAFT + "여야 한다.");
assertEquals(StudyStatus.DRAFT, study.studyStatus, "스터디를 처음 만들면 상태값이" + StudyStatus.DRAFT + "여야 한다.");
assertEquals(StudyStatus.DRAFT, study.studyStatus, "스터디를 처음 만들면 상태값이" + StudyStatus.DRAFT + "여야 한다.");
assertEquals(StudyStatus.DRAFT, study.studyStatus, "스터디를 처음 만들면 상태값이" + StudyStatus.DRAFT + "여야 한다.");
assertEquals(StudyStatus.DRAFT, study.studyStatus, "스터디를 처음 만들면 상태값이" + StudyStatus.DRAFT + "여야 한다.");
assertEquals(StudyStatus.DRAFT, study.studyStatus, "스터디를 처음 만들면 상태값이" + StudyStatus.DRAFT + "여야 한다.");
assertEquals(StudyStatus.DRAFT, study.studyStatus, "스터디를 처음 만들면 상태값이" + StudyStatus.DRAFT + "여야 한다.");
assertEquals(StudyStatus.DRAFT, study.studyStatus, "스터디를 처음 만들면 상태값이" + StudyStatus.DRAFT + "여야 한다.");
assertEquals(StudyStatus.DRAFT, study.studyStatus, "스터디를 처음 만들면 상태값이" + StudyStatus.DRAFT + "여야 한다.");
assertEquals(StudyStatus.DRAFT, study.studyStatus, "스터디를 처음 만들면 상태값이" + StudyStatus.DRAFT + "여야 한다.");
assertEquals(StudyStatus.DRAFT, study.studyStatus, "스터디를 처음 만들면 상태값이" + StudyStatus.DRAFT + "여야 한다.");
assertEquals(StudyStatus.DRAFT, study.studyStatus, "스터디를 처음 만들면 상태값이" + StudyStatus.DRAFT + "여야 한다.");
assertEquals(StudyStatus.DRAFT, study.studyStatus, "스터디를 처음 만들면 상태값이" + StudyStatus.DRAFT + "여야 한다.");
assertEquals(StudyStatus.DRAFT, study.studyStatus, "스터디를 처음 만들면 상태값이" + StudyStatus.DRAFT + "여야 한다.");
assertEquals(StudyStatus.DRAFT, study.studyStatus, "스터디를 처음 만들면 상태값이" + StudyStatus.DRAFT + "여야 한다.");
assertEquals(StudyStatus.DRAFT, study.studyStatus, "스터디를 처음 만들면 상태값이" + StudyStatus.DRAFT + "여야 한다.");
assertEquals(StudyStatus.DRAFT, study.studyStatus, "스터디를 처음 만들면 상태값이" + StudyStatus.DRAFT + "여야 한다.");
assertEquals(StudyStatus.DRAFT, study.studyStatus, "스터디를 처음 만들면 상태값이" + StudyStatus.DRAFT + "여야 한다.");
}
@Test
@DisplayName("assertEqualsSupplier 테스트")
void assertEqualsSupplierTest() {
Study study = new Study();
assertEquals(StudyStatus.DRAFT, study.studyStatus, () -> "스터디를 처음 만들면 상태값이" + StudyStatus.DRAFT + "여야 한다.");
assertEquals(StudyStatus.DRAFT, study.studyStatus, () -> "스터디를 처음 만들면 상태값이" + StudyStatus.DRAFT + "여야 한다.");
assertEquals(StudyStatus.DRAFT, study.studyStatus, () -> "스터디를 처음 만들면 상태값이" + StudyStatus.DRAFT + "여야 한다.");
assertEquals(StudyStatus.DRAFT, study.studyStatus, () -> "스터디를 처음 만들면 상태값이" + StudyStatus.DRAFT + "여야 한다.");
assertEquals(StudyStatus.DRAFT, study.studyStatus, () -> "스터디를 처음 만들면 상태값이" + StudyStatus.DRAFT + "여야 한다.");
assertEquals(StudyStatus.DRAFT, study.studyStatus, () -> "스터디를 처음 만들면 상태값이" + StudyStatus.DRAFT + "여야 한다.");
assertEquals(StudyStatus.DRAFT, study.studyStatus, () -> "스터디를 처음 만들면 상태값이" + StudyStatus.DRAFT + "여야 한다.");
assertEquals(StudyStatus.DRAFT, study.studyStatus, () -> "스터디를 처음 만들면 상태값이" + StudyStatus.DRAFT + "여야 한다.");
assertEquals(StudyStatus.DRAFT, study.studyStatus, () -> "스터디를 처음 만들면 상태값이" + StudyStatus.DRAFT + "여야 한다.");
assertEquals(StudyStatus.DRAFT, study.studyStatus, () -> "스터디를 처음 만들면 상태값이" + StudyStatus.DRAFT + "여야 한다.");
assertEquals(StudyStatus.DRAFT, study.studyStatus, () -> "스터디를 처음 만들면 상태값이" + StudyStatus.DRAFT + "여야 한다.");
assertEquals(StudyStatus.DRAFT, study.studyStatus, () -> "스터디를 처음 만들면 상태값이" + StudyStatus.DRAFT + "여야 한다.");
assertEquals(StudyStatus.DRAFT, study.studyStatus, () -> "스터디를 처음 만들면 상태값이" + StudyStatus.DRAFT + "여야 한다.");
assertEquals(StudyStatus.DRAFT, study.studyStatus, () -> "스터디를 처음 만들면 상태값이" + StudyStatus.DRAFT + "여야 한다.");
assertEquals(StudyStatus.DRAFT, study.studyStatus, () -> "스터디를 처음 만들면 상태값이" + StudyStatus.DRAFT + "여야 한다.");
assertEquals(StudyStatus.DRAFT, study.studyStatus, () -> "스터디를 처음 만들면 상태값이" + StudyStatus.DRAFT + "여야 한다.");
assertEquals(StudyStatus.DRAFT, study.studyStatus, () -> "스터디를 처음 만들면 상태값이" + StudyStatus.DRAFT + "여야 한다.");
assertEquals(StudyStatus.DRAFT, study.studyStatus, () -> "스터디를 처음 만들면 상태값이" + StudyStatus.DRAFT + "여야 한다.");
assertEquals(StudyStatus.DRAFT, study.studyStatus, () -> "스터디를 처음 만들면 상태값이" + StudyStatus.DRAFT + "여야 한다.");
assertEquals(StudyStatus.DRAFT, study.studyStatus, () -> "스터디를 처음 만들면 상태값이" + StudyStatus.DRAFT + "여야 한다.");
}
20개의 검증으로 실행할 경우
두 테스트 완료 시간이 5ms, 2ms로 차이가 나는 걸 볼 수 있다.
오늘은 Assertions에 대해서 알아보았습니다. 테스트 코드를 작성할 때 검증이 빠지면 테스트를 하는 이유가 없어지므로 매우 중요하기 때문에 꼭 알아야 한다고 생각합니다.
감사합니다.
출처:
더 자바, 애플리케이션을 테스트하는 다양한 방법
더 자바, 애플리케이션을 테스트하는 다양한 방법 - 인프런 | 강의
자바 프로그래밍 언어를 사용하고 있거나 공부하고 있는 학생 또는 개발자라면 반드시 알아야 하는 애플리케이션을 테스트하는 다양한 방법을 학습합니다., 그냥 개발자를 넘어 '더 나은 개발
www.inflearn.com
'Java > 더 자바, 애플리케이션을 테스트하는 다양한 방법' 카테고리의 다른 글
Junit5 테스트 인스턴스, 테스트 순서 (0) | 2023.09.12 |
---|---|
Junit5 테스트 반복하기 (0) | 2023.09.12 |
Junit5 태깅, 필터링 Tagging, Filtering (0) | 2023.09.11 |
Junit5 조건에 따라 테스트 실행하기 (0) | 2023.09.11 |
Junit5, 시작하기 (0) | 2023.09.08 |