Unit testing With BlockHound
Learn to run unit tests using BlockHound.
We'll cover the following...
Slipping BlockHound into the main entry point for our application is an effective warmup, but it’s not the best strategy. That approach leaves BlockHound active in production.
It’s better to leverage BlockHound in our test cases.
Add BlockHound to our application
To add BlockHound’s JUnit support, we add the following to our pom.xml
file:
<dependency><groupId>io.projectreactor.tools</groupId><artifactId>blockhound-junit-platform</artifactId><version>1.0.3.RELEASE</version><scope>test</scope></dependency>
This is required to bring in BlockHound and support for its JUnit Platform, TestExecutionListener
, meaning that it registers itself to look for blocking calls inside test methods.
What, exactly, is BlockHound looking for from a test-case perspective? Let’s start with a somewhat contrived example:
class BlockHoundUnitTest {// tag::obvious-failure[]@Testvoid threadSleepIsABlockingCall() {Mono.delay(Duration.ofSeconds(1)) // <1>.flatMap(tick -> {try {Thread.sleep(10); // <2>return Mono.just(true);} catch (InterruptedException e) {return Mono.error(e);}}).as(StepVerifier::create).verifyErrorMatches(throwable -> {assertThat(throwable.getMessage()).contains("Blocking call! java.lang.Thread.sleep");return true;});}// end::obvious-failure[]}
Here’s a breakdown of the code above:
-
In line 6, we insert a deliberate delay that forces this whole flow onto a Reactor thread, putting it into BlockHound’s scope.
-
In line 9,
Thread.sleep
is a blocking call because it ...