[Kotest] 시각을 고정시켜서 테스트하기

by 스뎅(thDeng) on

시간 관련된 코드를 작성하면 테스트를 위해 테스트를 실행하는 시각을 고정시켜야 할 때가 있다.

예를 들어, 심야 시간에 푸시를 보내지 않도록 하기 위해 현재 시각을 체크해서 오후 11시 부터 아침 8시 까지는 true를 반환하는 doNotDisturb 메소드를 만들었다고 하자. 이 메소드를 테스트하기 위해 밤낮으로 테스트를 돌릴 수 없으니 현재 시각을 고정해서 테스트를 진행하게 된다.

Kotest

withConstantNow 를 사용하면 현재 시각을 쉽게 고정할 수 있다.

class DisturbTimeTest : FunSpec({
    context("doNotDisturb") {
        test("오후 23시 부터 오전 8시 까지는 true 를 반환한다.") {
            forAll(
                row("2028-01-01T00:00:00", true),
                row("2028-01-01T01:23:45", true),
                row("2020-12-25T07:02:24", true),
                row("2020-12-31T07:59:59", true),
                row("2028-01-01T08:00:00", false),
                row("2022-04-01T12:00:00", false),
                row("2028-01-24T23:45:12", false),
                row("2026-01-01T23:00:01", true),
            ) { givenTime, expected ->
                val now = LocalDateTime.parse(givenTime)

                // 현재 시각 고정
                withConstantNow(now) {
                    doNotDisturb() shouldBe expected
                }
            }
        }
    }
}) {
    companion object {
        // 샘플을 위해 임의의 위치에 만든 테스트 대상
        fun doNotDisturb(): Boolean {
            // 현재 시각을 구해봅세!!
            val now = LocalDateTime.now()
            return now.toLocalTime().isBefore(LocalTime.of(8, 0, 0)) ||
                now.toLocalTime().isAfter(LocalTime.of(23, 0, 0))
        }
    }
}

doNotDisturb 메소드에서 시스템의 현재시각을 구해온다. 이 때 withConstantNow 를 사용하면 해당 블럭 안에서는 해당 시각으로 고정된다.

혹은 아래와 같은 방법으로 전역으로 테스트 시각을 고정시킬 수도 있다.

class Test : FunSpec() {
    override fun listeners() = listOf(
        ConstantNowTestListener(LocalDateTime.of(2020, 12, 25, 12, 34, 56))
    )

    init {
        // .. 테스트 코드 ..
    }
}

JUnit

JUnit 같은 경우, 시각을 고정하고 extension 등을 통해서 원래 시각으로 돌리게 된다.

@ExtendWith(value = [LocalDateTimeExtension::class])
class MyLocalDateTimeTest {
    fun `fixCurrentDateTime(LocalDateTime)`() {
        // given
        val now = LocalDateTime.of(2018, 12, 1, 12, 25, 38)

        // when
        LocalDateTimeTestHelper.fixCurrentDateTime(now)

        // then
        MyLocalDateTime.now() `should be equal to` now
    }
}

class LocalDateTimeExtension : AfterTestExecutionCallback {
    override fun afterTestExecution(context: ExtensionContext) {
        // 테스트가 끝날 때 마다 현재시각으로 돌린다.
        LocalDateTimeTestHelper.unfixCurrentDateTime()
    }
}

위 샘플에서 LocalDateTimeTestHelper#fixCurrentDateTime 을 통해서 현재 시각을 고정시키고, MyLocalDateTime 에서 고정된 instant를 가져오는 형태로 사용했다. 팀에서 JUnit을 사용할 때는 이 방법을 사용했었지만, Kotest 를 사용하니 MyLocalDateTime 클래스를 만드는 귀찮음도 없고 잘못해서 LocalDateTime 을 사용하면 테스트도 깨지고 번거로웠는데.. 너무 좋다 +_+ 이렇게 세상 편할수가!!

참고

별도로 명시하지 않을 경우, 이 블로그의 포스트는 다음 라이선스에 따라 사용할 수 있습니다: Creative Commons License CC Attribution-NonCommercial-ShareAlike 4.0 International License