Trait that defines an abstract patienceConfig
method that is implemented in PatienceConfiguration
and can
be overriden in stackable modification traits such as IntegrationPatience
.
Trait that facilitates testing with futures.
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:
1. Invoking isReadyWithin
, to assert that a future is ready within a a specified time period.
Here's an example:
assert(result.isReadyWithin(100 millis))
2. Invoking futureValue
, to obtain a futures result within a specified or implicit time period,
like this:
assert(result.futureValue === 7)
3. 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.ShouldMatchers._ 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.
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.
Stackable modification trait for PatienceConfiguration
that provides default timeout and interval
values appropriate for integration testing.
Stackable modification trait for PatienceConfiguration
that provides default timeout and interval
values appropriate for integration testing.
The default values for the parameters are:
Configuration Parameter | Default Value |
---|---|
timeout
|
scaled(15 seconds)
|
interval
|
scaled(150 milliseconds)
|
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.
Mix this trait into any class that uses PatienceConfiguration
(such as classes that mix in Eventually
or AsyncAssertions
) to get timeouts tuned towards integration testing, like this:
class ExampleSpec extends FeatureSpec with Eventually with IntegrationPatience { // ... }
Trait providing methods and classes used to configure timeouts and, where relevant, the interval between retries.
Trait providing methods and classes used to configure timeouts and, where relevant, the interval between retries.
This trait is called PatienceConfiguration
because it allows configuration of two
values related to patience: The timeout specifies how much time asynchronous operations will be given
to succeed before giving up. The interval specifies how much time to wait between checks to determine
success when polling.
The default values for timeout and interval provided by trait PatienceConfiguration
are tuned for unit testing,
where running tests as fast as
possible is a high priority and subsystems requiring asynchronous operations are therefore often replaced
by mocks. This table shows the default values:
Configuration Parameter | Default Value |
---|---|
timeout
|
scaled(150 milliseconds)
|
interval
|
scaled(15 milliseconds)
|
Values more appropriate to integration testing, where asynchronous operations tend to take longer because the tests are run
against the actual subsytems (not mocks), can be obtained by mixing in trait IntegrationPatience
.
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.
Timeouts are used by the eventually
methods of trait
Eventually
and the await
method of class
Waiter
, a member of trait
AsyncAssertions
. Intervals are used by
the eventually
methods.
Provides an implicit conversion from scala.concurrent.Future[T]
to
FutureConcept[T]
.
Provides an implicit conversion from scala.concurrent.Future[T]
to
FutureConcept[T]
.
This trait enables you to invoke the methods defined on FutureConcept
on a Scala Future
, as well as to pass a Scala future
to the whenReady
methods of supertrait Futures
.
See the documentation for supertrait Futures
for the details on the syntax this trait provides
for testing with Scala futures.
Trait providing a scaled
method that can be used to scale time
Span
s used during the testing of asynchronous operations.
Trait providing a scaled
method that can be used to scale time
Span
s used during the testing of asynchronous operations.
The scaled
method allows tests of asynchronous operations to be tuned
according to need. For example, Span
s can be scaled larger when running
tests on slower continuous integration servers or smaller when running on faster
development machines.
The Double
factor by which to scale the Span
s passed to
scaled
is obtained from the spanScaleFactor
method, also declared
in this trait. By default this method returns 1.0, but can be configured to return
a different value by passing a -F
argument to Runner
(or
an equivalent mechanism in an ant, sbt, or Maven build file).
The default timeouts and intervals defined for traits Eventually
and
AsyncAssertions
invoke scaled
, so those defaults
will be scaled automatically. Other than such defaults, however, to get a Span
to scale you'll need to explicitly pass it to scaled
.
For example, here's how you would scale a Span
you supply to
the failAfter
method from trait Timeouts
:
failAfter(scaled(150 millis)) { // ... }
The reason Span
s are not scaled automatically in the general case is
to make code obvious. If a reader sees failAfter(1 second)
, it will
mean exactly that: fail after one second. And if a Span
will be scaled,
the reader will clearly see that as well: failAfter(scaled(1 second))
.
spanScaleFactor
You can override the spanScaleFactor
method to configure the factor by a
different means. For example, to configure the factor from Akka
TestKit's test time factor you might create a trait like this:
import org.scalatest.concurrent.ScaledTimeSpans import akka.actor.ActorSystem import akka.testkit.TestKitExtension trait AkkaSpanScaleFactor extends ScaledTimeSpans { override def spanScaleFactor: Double = TestKitExtension.get(ActorSystem()).TestTimeFactor }
This trait overrides spanScaleFactor
so that it takes its
scale factor from Akka's application.conf
file.
You could then scale Span
s tenfold in Akka's configuration file
like this:
akka { test { timefactor = 10.0 } }
Armed with this trait and configuration file, you can simply mix trait
AkkaSpanScaleFactor
into any test class whose Span
s
you want to scale, like this:
class MySpec extends FunSpec with Eventually with AkkaSpanScaleFactor { // .. }
Companion object that facilitates the importing of ScalaFutures
members as
an alternative to mixing in the trait.
Companion object that facilitates the importing of ScalaFutures
members as
an alternative to mixing in the trait. One use case is to import ScalaFutures
's members so you can use
them in the Scala interpreter.
Trait that defines an abstract
patienceConfig
method that is implemented inPatienceConfiguration
and can be overriden in stackable modification traits such asIntegrationPatience
.The main purpose of
AbstractPatienceConfiguration
is to differentiate corePatienceConfiguration
traits, such asEventually
andAsyncAssertions
, from stackable modification traits forPatienceConfiguration
s such asIntegrationPatience
. Because these stackable traits extendAbstractPatienceConfiguration
instead ofSuite
, you can't simply mix in a stackable trait:The previous code is undesirable because
IntegrationPatience
would have no affect on the class. Instead, you need to mix in a corePatienceConfiguration
trait and mix the stackableIntegrationPatience
trait into that, like this:The previous code is better because
IntegrationPatience
does have an effect: it modifies the behavior ofEventually
.