Waiting for that important call
Tags: javascript, testing
Published: 2018-03-14 13:42:31

---
title: Waiting for that important call
published: true
description: Pause a test until a function has been called.
tags: javascript, testing
cover_image: https://thepracticaldev.s3.amazonaws.com/i/n6kbdkm2902takaujwqp.jpg
canonical_url: https://www.rmkennedy.com/posts/anticipated-call
---

Sometimes while testing, it's necessary to wait until a function has been called. Maybe you're testing code with [Node-style callbacks](http://fredkschott.com/post/2014/03/understanding-error-first-callbacks-in-node-js/); maybe you're working with a [React render prop](https://reactjs.org/docs/render-props.html). Regardless of how you got there, your  test needs to pause until some function has been called. It's possible to wait for a promise to be fulfilled, but how do you wait until an arbitrary function has been called?

## The problem

Suppose your test looks like this:

```js
const createEmitterOfSomeSort = require('./myEmitter');

it('should do the thing', async () => {
  const emitter = createEmitterOfSomeSort();
  const callback = jest.fn();
  emitter.on('my-event', callback);
  
  // TODO: wait for the callback to be called before proceeding

  // Check values which will only change after the given event
  expect(emitter.color).toBe('blue');
});
```

This test needs to wait for `my-event` to be fired asynchronously before the color gets set. Otherwise, the test prematurely races through to its completion.

It's possible to wrap this all in a Promise which will resolve when your event is fired. I've done this loads of times in tests; it's tedious! It's also a pain to refactor. Suppose you want to wait for the event to fire 5 times instead of just once. This requires additional work and added complexity to your test.

## My attempted solution

I decided to write and publish my solution as the [`anticipated-call`](https://www.npmjs.com/package/anticipated-call) package. This utility is capable of wrapping any function, and gives you an easy way to obtain a promise which resolves once the function has been called.

Here's an example of how you might use it in a test:

```js
const anticipated = require('anticipated-call');
const createEmitterOfSomeSort = require('./myEmitter');

it('should do the thing', async () => {
  const emitter = createEmitterOfSomeSort();
  const callback = anticipated(jest.fn());
  emitter.on('my-event', callback);
  
  await callback.nextCall;

  // Check values which will only change after the given event
  expect(emitter.color).toBe('blue');
});
```

The `await` statement is the magic sauce: it'll pause the test's execution until the callback is called.

Now, if you decide the event needs to be fired 5 times instead of just once, it's simple to update your tests:

```js
  await callback.nthNextCall(5);
```

## Testing React render props

This package has helped me the most when I'm writing render-prop components. Suppose you have a component responsible for fetching data that's used like this:

```jsx
(<MyTweetFetcher
  render={({isLoading, username, tweets}) => (
    <h2>{isLoading ? 'Loading...' : username}</h2>
    <ul>
      {tweets.map((tweet) => (
        <li key={tweet.id}>{tweet.content}</li>
      )}
    </ul>
  )
/>)
```

These components commonly call the render prop multiple times in response to asynchronous operations. This behavior creates a problem for writing tests: you need to make sure that the callback received the correct arguments, but you can't perform that check until the component has been rendered. `anticipated-call` comes to the rescue:

```jsx
const Enzyme = require('enzyme');
const anticipated = require('anticipated-call');

const MyTweetFetcher = require('./MyTweetFetcher');

it('should call the render prop with the correct arguments', async () => {
  // The render prop needs to return a valid React node, so use `null` here.
  const renderProp = anticipated(jest.fn(() => null));

  // The `nextCallDuring` method allows you to tell `anticipated-call` that
  // the function should be called as a result of running the passed callback.
  await renderProp.nextCallDuring(() => {
    Enzyme.mount(<MyTweetFetcher render={renderProp} />);
  });
  
  // The render prop will initially be called while data is loading.
  expect(renderProp.mock.calls[0].isLoading).toBe(true);

  // Wait for the render prop to be called again, after the data has loaded.
  await renderProp.nextCall;

  expect(renderProp.mock.calls[1].isLoading).toBe(false);
  expect(renderProp.mock.calls[1].tweets).toBeInstanceOf(Array);
});
```

## Friendlier testing

This package is [pretty small](https://github.com/r24y/anticipated-call/blob/develop/src/index.js); it does nothing that can't already be done with a bit of Promise-wrangling. However, its appeal lies in the fact that you no longer _have_ to engage in any Promise-wrangling. When I need to wait for a callback, I throw `anticipated-call` at it and save my energy for more difficult problems.

> Check out [`anticipated-call`](https://www.npmjs.com/package/anticipated-call) on npm and submit PRs or issues on [Github](https://github.com/r24y/anticipated-call) if you have ideas for improving it!