ScalaTest: lock assertion
I am using code with expression blocking
:
blocking {
Thread.sleep(10*1000)
}
Can it be argued that this operator is blocking
specified? Or in other words: can I write a test that fails if someone removes the statement blocking
?
Update: How to assert a lock when used in Future
s?
source to share
Try playing with BlockContext .
You should get something like this:
var blocked = false // flag to detect blocking
val oldContext = BlockContext.current
val myContext = new BlockContext {
override def blockOn[T](thunk: =>T)(implicit permission: CanAwait): T = {
blocked = true
oldContext.blockOn(thunk)
}
}
BlockContext.withBlockContext(myContext) {
blocking {} // block (or not) here
}
assert(blocked) // verify that blocking happened
Update while working with it if you want to test the code wrapped in Future
(comment)
When constructed, Future
a factory method takes a block of code (functions) to execute explicitly and an execution context implicitly (usually scala.concurrent.ExecutionContext.Implicits.global
).
The block of code will later be assigned to the execution context and run in one of its threads.
Now if you just end up locking the code snippet into a Future code block inside the code passed in BlockContext.withBlockContext
, as you suggest in the comment:
BlockContext.withBlockContext(myContext) {
Future {
blocking { Thread.sleep(100) }
}
}
... this won't work, since your current thread will only execute the Future
Construct and the actual code passed to Future
will execute on the thread from the appropriate execution context ( BlockContext.withBlockContext
defines blocking
in the current thread).
Having said that, I can suggest that you do one of three things:
-
Do not complete any code you want to test in the future. If you want to check if a piece of code is using
blocking
or not, just do that.
Write a function and test it, you can pass it toFuture
in production. -
Let's assume that for some reason you cannot avoid creating a Future in your test. In this case, you will have to tamper with the execution context that is used when constructing the future.
This code example demonstrates how it can be done (reusedblocked
andmyContext
from my original example):
// execution context that submits everything that is passed to it to global execution context
// it also wraps any work submited to it into block context that records blocks
implicit val ec = new ExecutionContext {
override def execute(runnable: Runnable): Unit = {
ExecutionContext.Implicits.global execute new Runnable {
override def run(): Unit = {
BlockContext.withBlockContext(myContext) {
runnable.run()
}
}
}
}
override def reportFailure(t: Throwable): Unit = {
ExecutionContext.Implicits.global.reportFailure(t)
}
}
// this future will use execution context defined above
val f = Future {
blocking {} // block (or not) here
}
Await.ready(f, scala.concurrent.duration.Duration.Inf)
assert(blocked)
- If yours
Future
is being instantiated indirectly, for example as a result of calling some other function that you run in your test, you will have to somehow (perhaps using dependency injection) drag your mocked execution context to where itFuture
is instantiated and uses it there to create it.
As you can see, the first option is the simplest, and I suggest sticking with it if you can.
source to share