Unwrap your futures while testing


Futures in Scala are a very powerful construct. Using map and flatMap, just to name a couple, we can specify code that will be asynchronously executed only when a Future is completed successfully. In case of a failure, the code won’t be executed and these function return the same failed future. This allows us not to be worried about handling failures when dealing with Future execution results, and to write truly asynchronous code.

However, there are cases in which map is not the most appropriate choice.

Let’s say, for example, we wrote a very complex algorithm that gives an answer to the ultimate question of life, the universe and everything.

def answer: Future[Int] = Future.successful(42)

Great! But we’re one step ahead and we knew already the answer to this question. Hence, we just want to test that the answer is the right one, for example using ScalaTest matchers (for simplicity I’m only specifying the assertions here).

We could map over it:

answer.map { result =>
  result shouldBe 42
}

Great! Our test passes.

However, as we’re developers and not fools, we don’t believe a test that we never saw failing (right?), so we write a test that should fail.

answer.map { result =>
  result shouldBe -1
}

Ouch! This passes as well. Why?

Code in the map block will be executed asynchronously and in another thread. Tests framework such as ScalaTest work on throwing exceptions for test failures, and unfortunately this map approach simply doesn’t work.

In order to test Future values, ScalaTest offers a trait called ScalaFutures, which defines an implicit conversion to FutureConcept which offers a futureValue method:

answer.futureValue shouldBe 42

This assertion will work, and it will correctly fail when changing from 42 to -1. futureValue takes an implicit PatienceConfiguration which gives instructions on how long to wait and how often to check for completion, and it either returns the value of the successfully completed future, or throws the exception the future has failed with (or fails the test in case the future is not completed within the given timeout). So, basically, it’s unwrapping the future for us to have a look at what’s inside.

While the example showed above is related to a specific test framework, the rationale behind it is universal. While composing futures is always a good idea for production code, there are certain situations in which we want to actually wait for a future to be completed before proceeding. Such as in a test.