When you run a specification, a java.util.concurrent.ExecutorService
is used to execute examples concurrently. You can access this ExecutorService
to execute scalaz.concurrent.Futures
and it can also be wrapped into a scala.concurrent.ExecutionContext
to create scala.concurrent.Futures
.
A Scala Future
needs an implicit ExecutionContext
to be created. You can get an execution context shared across all specifications by declaring it as a class member:
class MyFutureSpec(implicit ec: ExecutionContext) extends Specification { def is = s2"""
Let's check this scala future ${
Await.result(Future(1), Duration.Inf) must_== 1
}
"""
}
// in a mutable specification
class MyMutableFutureSpec(implicit ec: ExecutionContext) extends mutable.Specification {
"Let's check this scala future" >> {
Await.result(Future(1), Duration.Inf) must_== 1
}
}
You can also use an ExecutionEnv
(from now on code examples are provided for immutable specifications only but are transposable to mutable ones):
class MyFutureSpec(implicit ee: ExecutionEnv) extends Specification { def is = s2"""
Let's check this scala future ${
Await.result(Future(1), Duration.Inf) must_== 1
}
"""
}
This works thanks to an implicit conversion between ExecutionEnv
and ExecutionContext
provided by the org.specs2.execute.ImplicitExecutionContextFromExecutionEnv
trait (this can be deactivated by mixing-in the NoImplicitExecutionContextFromExecutionEnv
trait). It is actually better to use an ExecutionEnv
anyway because it is required when you want to create Future
matchers (see the “Future” tab). Indeed an ExecutionEnv
contains a timeFactor
which can be used to modify the timeout from the command line and wait longer for Futures executing on a continuous integration server for example.
A Scalaz Future
needs an implicit ExecutorService
to evaluate values asynchronously. You can require an ExecutorService
in your examples like this:
class MyFutureSpec(implicit es: ExecutorService) extends Specification { def is = s2"""
Let's check this scalaz future ${
scalaz.concurrent.Future(1).run must_== 1
}
"""
}
And, similarly to the section above, you can get an implicit ExecutorService
from an ExecutionEnv
:
class MyFutureSpec(implicit ee: ExecutionEnv) extends Specification { def is = s2"""
Let's check this scalaz future ${
scalaz.concurrent.Future(1).run must_== 1
}
"""
}
You will also need a ScheduledExecutorService
if you want to evaluate a Scalaz Future using the timed
method (from Scalaz > 7.1):
class MyFutureSpec(implicit ee: ExecutionEnv) extends Specification { def is = s2"""
Let's check this scalaz future ${
implicit val ses = ee.scheduledExecutorService
scalaz.concurrent.Future(1).timed(3.seconds).run.toOption must beSome(1)
}
"""
}
Future Matchers (see the “Future” tab) require an implicit ExecutionEnv
. This environment is used to access:
timeFactor
when awaiting for Scala FuturesscheduledExecutorService
and the timeFactor
when attempting Scalaz FuturesThe terminate
matcher (see the “Termination” tab in the optional Matchers section) also needs an ExecutionEnv
to run a piece of code and periodically check if it has terminated or not:
s2"""
this code must be fast enough ${
implicit val ee = ExecutionEnv.fromGlobalExecutionContext
Thread.sleep(100) must terminate(retries = 1, sleep = 60.millis)
}
"""
If you want to have exactly one Env
or one ExecutionEnv
per Specification
you can mix-in the org.specs2.specicication.core.OwnEnv
or the org.specs2.specicication.core.OwnExecutionEnv
traits. You will then get a specific threadpool instantiated and shutdown just for the execution of one specification. See the environment page for more information.