Testing Asynchronous Code

Learn how to test asynchronous code in Scala with ScalaTest.

Synchronous vs. asynchronous code

So far, we’ve only been testing synchronous code; every method and test we’ve written has been blocking and not asynchronous. This isn’t very realistic in a modern application since many operations have to be non-blocking. Think of downloading a file from somewhere, querying a database, or waiting for a response from another application on the internet. If those operations were blocking, our applications would be incredibly slow, and we’d waste tons of time waiting for results.

The basic concurrency component in Scala is the Future class. An instance of Future is nothing more than a placeholder for a value that will be ready sometime in the future. Using Future leads to faster code that can be run in a non-blocking way. In this lesson and the next, we’ll get a glimpse of how to test asynchronous code in Scala and ScalaTest. Covering the Future execution model in ScalaTest in detail is outside the scope of this course because it requires a deeper understanding of the concurrency model in Scala. In the next sections, we’ll see different possibilities for Future in our tests without focusing too much on the underlying mechanics.

Note: The Future class is sometimes considered a low-level construct that shouldn’t be used directly in an application. We tend to agree, and if you can, you should use higher-level concurrent libraries in your code rather than reinventing the wheel. However, these libraries are beyond the scope of this course, so we’ll focus on Future here.

First, we’ll define a new repository named CourseRepository, which is supposed to load the details of the courses.

Press + to interact
import mdipirro.educative.io.effectiveunitandintegrationtestinginscala.model.v2.Course
import scala.concurrent.{ExecutionContext, Future}
trait CourseRepository:
def getAll: Future[Seq[Course]]
class SimpleCourseRepository(using ExecutionContext):
def getAll: Future[Seq[Course]] = Future { Seq.empty }

The CourseRepository is a trait defining just one method, getAll, returning a Seq[Course]. On the other hand, SimpleCourseRepository is just a dummy implementation of that trait, returning an empty sequence of the Course class. The SimpleCourseRepository class’s override of getAll wouldn’t actually need to return a Future since it returns a constant value, but let’s assume that Seq.empty is a time-consuming operation involving querying a database.

Note that SimpleCourseRepository requires an ExecutionContext to create a Future. An ExecutionContext in Scala is a way to specify how program logic should be executed at runtime.

Testing Future while blocking

The simplest way for us to test a portion of asynchronous code is blocking as usual. In Scala we can explicitly wait for the result of a Future using Await, a Scala object exposing two methods: ready ...