Search code examples
scalaasynchronousfuturescalatest

Why my Scala Async test never completes when I do Await.result?


I have created a simple test scenario that shows this:

class Test extends AsyncFunSuite {

  test("async test") {
    val f = Future {
      val thread = new Thread {
        override def run(): Unit = {
          println("OKAYY")
        }
      }
      thread.start()
    }

    Await.result(f, Duration.Inf)
    Future {
      assert(true)
    }
  }

}

When executing this, the test runs forever and never completes.


Solution

  • You will see the same behavior with all asynchronous testing in ScalaTest (AsyncFeatureSpec, AsyncFlatSpec, AsyncFreeSpec, AsyncFunSpec, AsyncFunSuite, AsyncWordSpec).

    The reason for this is that the default execution context in ScalaTest is serial execution context. You can read more about this here: https://www.scalatest.org/user_guide/async_testing. I have summarized the important points below.

    Using ScalaTest's serial execution context on the JVM will ensure the same thread that produced the Future[Assertion] returned from a test body is also used to execute any tasks given to the execution context while executing the test body—and that thread will not be allowed to do anything else until the test completes.

    This thread confinement strategy does mean, however, that when you are using the default execution context on the JVM, you must be sure to never block in the test body waiting for a task to be completed by the execution context. If you block, your test will never complete.

    Solution 1: Override execution context

    implicit override def executionContext = scala.concurrent.ExecutionContext.Implicits.global
    

    Solution 2: Chaining

    class Test extends AsyncFunSuite {
    
      test("async test") {
        val f = Future {
          val thread = new Thread {
            override def run(): Unit = {
              println("OKAYY")
            }
          }
          thread.start()
        }
    
        f.map { _ =>
          assert(true)
        }
      }
    
    }