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?

+3


source to share


1 answer


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 to Future

    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 (reused blocked

    and myContext

    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)

      

  1. 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 it Future

    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.

+3


source







All Articles