Image by Freepik
비동기적으로 동작하는 코드를 테스트 하기 위해서는 시간이라는 변수를 다룰 수 있어야 합니다. 크게 아래 3가지 범주의 요구가 있습니다.
Awaitility 라는 도구를 사용하면 비동기 코드의 타이밍을 쉽게 테스트할 수 있습니다. 이제 위 세 가지 요구사항을 테스트하기 위한 Awaitility 예제 코드를 작성해 보겠습니다. 각 요구사항 별로 성공 및 실패 케이스에 대한 테스트 코드를 작성합니다.
전체 코드는 GitHub에서 확인할 수 있습니다.
atMost() 메서드를 활용하면 특정 시간 이내에 조건을 만족하는지 확인할 수 있습니다.
@Test
void atMostSuccess() {
AtomicInteger value = new AtomicInteger(0);
// When: 1초 후에 조건 만족하도록 설정
executor.submit(() -> {
Thread.sleep(1000);
value.set(1);
return true;
});
// Then: 2초 이내에 조건을 만족하여 성공
await().atMost(2000, TimeUnit.MILLISECONDS)
.until(() -> value.get() == 1);
}
@Test
void atMostFail() {
AtomicInteger value = new AtomicInteger(0);
// When: 1초 후에 조건 만족하도록 설정
executor.submit(() -> {
Thread.sleep(1000);
value.set(1);
return true;
});
// Then: 500msec 이내에 조건을 만족하지 못해 예외 발생
assertThrows(ConditionTimeoutException.class, () -> {
await().atMost(500, TimeUnit.MILLISECONDS)
.until(() -> value.get() == 1);
});
}
atLeast() 메서드를 활용하면 특정 시간 이후에 조건을 만족하는지 확인할 수 있습니다. 다르게 생각하면 특정 시간 이전에는 조건을 만족해서는 안되는 상황을 테스트할 수 있습니다.
@Test
void atLeastSuccess() {
AtomicInteger value = new AtomicInteger(0);
// When: 1초 후에 조건 만족하도록 설정
executor.submit(() -> {
Thread.sleep(1000);
value.set(1);
return true;
});
// Then: 1초 이후에 조건을 만족하여 성공
await().atLeast(900, TimeUnit.MILLISECONDS)
.until(() -> value.get() == 1);
}
@Test
void atLeastFail() {
AtomicInteger value = new AtomicInteger(0);
// When: 1초 후에 조건 만족하도록 설정
executor.submit(() -> {
Thread.sleep(1000);
value.set(1);
return true;
});
// Then: 1.1초 이전에 조건을 만족하여 예외 발생
assertThrows(ConditionTimeoutException.class, () -> {
await().atLeast(1100, TimeUnit.MILLISECONDS)
.until(() -> value.get() == 1);
});
}
during() 메서드를 활용하면 특정 기간 동안 연속해서 조건을 만족하는지 확인할 수 있습니다.
@Test
void duringSuccess() {
AtomicInteger value = new AtomicInteger(0);
// When: 500ms 동안만 조건을 만족하는 작업 실행
executor.submit(() -> {
value.set(1);
Thread.sleep(500);
value.set(0);
Thread.sleep(500);
return true;
});
// Then: 약 500ms 동안 조건을 만족
await().during(400, TimeUnit.MILLISECONDS)
.atMost(1000, TimeUnit.MILLISECONDS)
.until(() -> value.get() == 1);
}
@Test
void duringFail() {
// Given
AtomicInteger value = new AtomicInteger(0);
// When: 300ms 동안만 조건을 만족하는 작업 실행
executor.submit(() -> {
value.set(1);
Thread.sleep(300);
value.set(0);
Thread.sleep(700);
return true;
});
// Then: 약 500ms 동안 조건 만족 실패
assertThrows(ConditionTimeoutException.class, () -> {
await().during(400, TimeUnit.MILLISECONDS)
.atMost(1000, TimeUnit.MILLISECONDS)
.until(() -> value.get() == 1);
});
}
Awaitility 라는 프레임워크를 알게되어 감사합니다. 짧은 글이지만 필요한 분들께 도움이 되면 좋겠습니다.