Trait that facilitates testing with futures.
This trait defines a FutureConcept
trait that can be used to implicitly wrap different kinds of futures, thereby providing a uniform testing API for futures. The three ways this trait enables you to test futures are:
- Invoking
isReadyWithin
, to assert that a future is ready within a a specified time period. Here's an example:
assert(result.isReadyWithin(100 millis))
- Invoking
futureValue
, to obtain a futures result within a specified or implicit time period, like this:
assert(result.futureValue === 7) // Or, if you expect the future to fail: assert(result.failed.futureValue.isInstanceOf[ArithmeticException])
- Passing the future to
whenReady
, and performing assertions on the result value passed to the given function, as in:
whenReady(result) { s => s should be ("hello") }
The whenReady
construct periodically inspects the passed future, until it is either ready or the configured timeout has been surpassed. If the future becomes ready before the timeout, whenReady
passes the future's value to the specified function.
To make whenReady
more broadly applicable, the type of future it accepts is a FutureConcept[T]
, where T
is the type of value promised by the future. Passing a future to whenReady
requires an implicit conversion from the type of future you wish to pass (the modeled type) to FutureConcept[T]
. Subtrait JavaFutures
provides an implicit conversion from java.util.concurrent.Future[T]
to FutureConcept[T]
.
For example, the following invocation of whenReady
would succeed (not throw an exception):
import org.scalatest._ import Matchers._ import concurrent.Futures._ import java.util.concurrent._ val exec = Executors.newSingleThreadExecutor val task = new Callable[String] { def call() = { Thread.sleep(50); "hi" } } whenReady(exec.submit(task)) { s => s should be ("hi") }
However, because the default timeout is 150 milliseconds, the following invocation of whenReady
would ultimately produce a TestFailedException
:
val task = new Callable[String] { def call() = { Thread.sleep(500); "hi" } } whenReady(exec.submit(task)) { s => s should be ("hi") }
Assuming the default configuration parameters, a timeout
of 150 milliseconds and an interval
of 15 milliseconds, were passed implicitly to whenReady
, the detail message of the thrown TestFailedException
would look like:
The future passed to whenReady was never ready, so whenReady timed out. Queried 95 times, sleeping 10 milliseconds between each query.
== Configuration of whenReady
==
The whenReady
methods of this trait can be flexibly configured. The two configuration parameters for whenReady
along with their default values and meanings are described in the following table:
Configuration Parameter | Default Value | Meaning |
---|---|---|
timeout | scaled(150 milliseconds) | the maximum amount of time to allow unsuccessful queries before giving up and throwing TestFailedException |
interval | scaled(15 milliseconds) | the amount of time to sleep between each query |
The default values of both timeout and interval are passed to the scaled
method, inherited from ScaledTimeSpans
, so that the defaults can be scaled up or down together with other scaled time spans. See the documentation for trait ScaledTimeSpans
for more information.
The whenReady
methods of trait Futures
each take a PatienceConfig
object as an implicit parameter. This object provides values for the two configuration parameters. Trait Futures
provides an implicit val
named defaultPatience
with each configuration parameter set to its default value. If you want to set one or more configuration parameters to a different value for all invocations of whenReady
in a suite you can override this val (or hide it, for example, if you are importing the members of the Futures
companion object rather than mixing in the trait). For example, if you always want the default timeout
to be 2 seconds and the default interval
to be 5 milliseconds, you can override defaultPatience
, like this:
implicit override val defaultPatience = PatienceConfig(timeout = Span(2, Seconds), interval = Span(5, Millis))
Or, hide it by declaring a variable of the same name in whatever scope you want the changed values to be in effect:
implicit val defaultPatience = PatienceConfig(timeout = Span(2, Seconds), interval = Span(5, Millis))
In addition to taking a PatienceConfig
object as an implicit parameter, the whenReady
methods of trait Futures
include overloaded forms that take one or two PatienceConfigParam
objects that you can use to override the values provided by the implicit PatienceConfig
for a single whenReady
invocation. For example, if you want to set timeout
to 6 seconds for just one particular whenReady
invocation, you can do so like this:
whenReady (exec.submit(task), timeout(Span(6, Seconds))) { s => s should be ("hi") }
This invocation of eventually
will use 6000 for timeout
and whatever value is specified by the implicitly passed PatienceConfig
object for the interval
configuration parameter. If you want to set both configuration parameters in this way, just list them separated by commas:
whenReady (exec.submit(task), timeout(Span(6, Seconds)), interval(Span(500, Millis))) { s => s should be ("hi") }
You can also import or mix in the members of SpanSugar
if you want a more concise DSL for expressing time spans:
whenReady (exec.submit(task), timeout(6 seconds), interval(500 millis)) { s => s should be ("hi") }
Note: The whenReady
construct was in part inspired by the whenDelivered
matcher of the BlueEyes project, a lightweight, asynchronous web framework for Scala.
Attributes
- Companion
- object
- Graph
-
- Supertypes
- Known subtypes