6 Unit testing asynchronous code

 

This chapter covers

  • Async, done(), and awaits
  • Integration and unit test levels for async
  • The Extract Entry Point pattern
  • The Extract Adapter pattern
  • Stubbing, advancing, and resetting timers

When we’re dealing with regular synchronous code, waiting for actions to finish is implicit. We don’t worry about it, and we don’t really think about it too much. When dealing with asynchronous code, however, waiting for actions to finish becomes an explicit activity that is under our control. Asynchronicity makes code, and the tests for that code, potentially trickier because we have to be explicit about waiting for actions to complete.

Let’s start with a simple fetching example to illustrate the issue.

6.1 Dealing with async data fetching

Let’s say we have a module that checks whether our website at example.com is alive. It does this by fetching the context from the main URL and checking for a specific word, “illustrative,” to determine if the website is up. We’ll look at two different and very simple implementations of this functionality. The first uses a callback mechanism, and the second uses an async/await mechanism.

Figure 6.1 illustrates their entry and exit points for our purposes. Note that the callback arrow is pointed differently, to make it more obvious that it’s a different type of exit point.

Figure 6.1 IsWebsiteAlive() callback vs. the async/await version
06-01

6.1.1 An initial attempt with an integration test

6.1.2 Waiting for the act

6.1.3 Integration testing of async/await

6.1.4 Challenges with integration tests

6.2 Making our code unit-test friendly

6.2.1 Extracting an entry point

6.2.2 The Extract Adapter pattern