Using futures in eventually with an asynchronous ScalaTest

In ScalaTest you can use asynchronous specs, like AsyncWordSpec. In the tests you can then use the assertions like you do in a non-asynchronous spec, like WordSpec, but if a method you want to test returns a Future, you can map the result of the method to an assertion. E.g.:
class AsyncTestSpec extends AsyncWordSpec with Matchers {
"the method" should {
"return the expected result" in {
determineValue.map(_ shouldBe 1)
}
}
}
In the example above, determineValue return a Future[Int]. If the method that you want to test eventually returns the expected value, but initially not yet, perhaps due to initialization, you would normally use Eventually. Using eventually in an asynchronous test however, doesn’t work as expected, at least in ScalaTest 3.0: the test isn’t executed repeatedly. So the following test is only tried once and not repeatedly like you would expect when using eventually:
class AsyncTestSpec extends AsyncWordSpec with Eventually with Matchers {

private val atMost = 1.second

"the method" should {
"return the expected result" in {
eventually {
determineValue.map(_ shouldBe 1)
}
}
}
The easiest way to solve this, is not using an asynchronous spec with eventually: e.g. use a WordSpec instead and use Await.result in the eventually block. If for some reason, you need to use an asynchronous spec, you must not only use Await.result in the eventually block, but you must also explicitly give an execution context to the method that returns the Future: using the execution context of the asynchronous spec will result in a test that fails because the execution took too long. A complete example of using eventually with an asynchronous spec:
class AsyncTestSpec extends AsyncWordSpec with Eventually with Matchers {
private val customExecutionContext: ExecutionContext = ExecutionContext.fromExecutor(new ForkJoinPool())

private val atMost = 1.second

"the method" should {
"eventually return the expected result" in {
eventually {
implicit val executionContext: ExecutionContext = customExecutionContext
Await.result(determineValue, atMost) shouldBe 1
}
}
}
}
In the example above, determineValue has an implicit execution context as parameter.

Leave a Reply

Your email address will not be published.