Kotest BlockHound 확장

BlockHound 확장에 대해 소개한다.

Kotest BlockHound 확장 프로그램은 코루틴에 대한 BlockHound 지원을 활성화한다. 이 확장 기능은 차단되지 않는 코루틴 스레드에서 차단 코드를 탐지하는 데 도움이 된다(예: UI 스레드에서 실수로 차단하는 I/O 라이브러리 함수를 호출하는 경우).

Latest Release

시작하기

테스트 클래스에 BlockHound 확장을 등록한다:

class BlockHoundSpecTest : FunSpec({
   extension(BlockHound())

   test("detects for spec") {
      blockInNonBlockingContext()
   }
})

BlockHound 확장은 테스트 케이스별로 또는 프로젝트 구성에서 등록할 수도 있다.

프로젝트 전체 또는 사양 전체에서 BlockHound가 활성화된 경우 개별 테스트에 대해 비활성화할 수 있다:

   test("allow blocking").config(extensions = listOf(BlockHound(BlockHoundMode.DISABLED))) {
      blockInNonBlockingContext()
   }

코드 섹션의 BlockHoundMode를 변경할 수도 있다:

   test("allow blocking section") {
      // ...
      withBlockHoundMode(BlockHoundMode.DISABLED) {
        blockInNonBlockingContext()
      }
      // ...
   }

탐지(Detection)

차단 호출은 차단하지 않을 것으로 예상되는 코루틴 스레드에서 탐지된다. 이러한 스레드는 이 예제에서 볼 수 있듯이 기본 디스패처에 의해 생성된다:

private suspend fun blockInNonBlockingContext() {
   withContext(Dispatchers.Default) {
      @Suppress("BlockingMethodInNonBlockingContext")
      Thread.sleep(2)
   }
}

BlockHound 확장 프로그램은 기본적으로 차단 호출를 탐지할 때마다 이와 같은 예외를 생성한다:

reactor.blockhound.BlockingOperationError: Blocking call! java.lang.Thread.sleep
    at io.kotest.extensions.blockhound.KotestBlockHoundIntergration.applyTo$lambda-2$lambda-1(KotestBlockHoundIntergration.kt:27)
    at reactor.blockhound.BlockHound$Builder.lambda$install$8(BlockHound.java:427)
    at reactor.blockhound.BlockHoundRuntime.checkBlocking(BlockHoundRuntime.java:89)
    at java.base/java.lang.Thread.sleep(Thread.java)
    at io.kotest.extensions.blockhound.BlockHoundTestKt$blockInNonBlockingContext$2.invokeSuspend(BlockHoundTest.kt:17)
    at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
    at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
    at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:570)
    at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:750)
    at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:677)
    at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:664)

블록킹 호출이 감지되면 다음과 같이 할 수 있다.

  • 호출을 논블로킹 호출로 대체하거나(코루틴 인식 라이브러리 사용), 또는
  • 호출 코루틴이 별도의 I/O 스레드에서 실행되도록 예약하거나(예: Dispatchers.IO를 통해), 또는
  • 블로킹이 무해한 경우 예외를 추가한다(아래 참조).

사용자 지정

BlockHound를 사용자 지정하려면 BlockHound 문서를 숙지한다.

무해한 것으로 간주되는 호출을 차단하는 예외는 다음과 같이 별도의 BlockHoundIntergration 클래스를 통해 추가할 수 있다:

import reactor.blockhound.BlockHound
import reactor.blockhound.intergration.BlockHoundIntergration

class MyBlockHoundIntergration : BlockHoundIntergration {
   override fun applyTo(builder: BlockHound.Builder): Unit = with(builder) {
      allowBlockingCallsInside("org.slf4j.LoggerFactory", "performInitialization")
   }
}

BlockHound가 통합을 자동으로 감지하고 로드할 수 있도록 하려면, 서비스 공급자 구성 파일 resources/META-INF/services/reactor.blockhound.intergration.BlockHoundIntergration에 정규화된 클래스 이름을 추가한다.


참조




최종 수정 : 2024-04-14