Testing the onMount callback
Tags: svelte, testing, tdd, series
Published: 2020-01-12 14:04:44

---
title: Testing the onMount callback
published: true
description: Learn how to test the onMount callback in your code
tags: svelte, testing, tdd, series
series: Writing unit tests for Svelte
---

Welcome back to the _Writing unit tests for Svelte series_! Thanks for sticking with me! ❤️

In this part we’ll test an asynchronous `onMount` callback. It’s going to be a super short one!

As ever, you can refer to the GitHub repository for all of the code samples.

{% github dirv/svelte-testing-demo no-readme %}

*Important*: What we’re about to do is something I don’t recommend doing in your production code. In the interests of simple testing, you should move all business logic out of components. In the next part we’ll look at a better way of achieving the same result.

### Checking that a callback was called

Let’s started by defining the component. This is `src/CallbackComponent.svelte`:

```html
<script>
  import { onMount } from "svelte";

  let price = '';

  onMount(async () => {
    const response = await window.fetch("/price", { method: "GET" });
    if (response.ok) {
      const data = await response.json();
      price = data.price;
    }
  });

</script>

<p>The price is: ${price}</p>
```

To test this, we’re going to stub out `window.fetch`. Jasmine has in-built spy functionality&mdash;the `spyOn` function&mdash;which is essentially the same as Jest’s `spyOn` function. If you’re using Mocha I suggest using the [sinon](https://sinonjs.org/) library (which, by the way, has really fantastic documentation on test doubles in general).

When I mock out the fetch API I always like to use this helper function:

```javascript
const fetchOkResponse = data =>
  Promise.resolve({ ok: true, json: () => Promise.resolve(data) });
```

You can define a `fetchErrorResponse` function in the same way, although I’m going to skip that for this post.

You can use this to set up a stub for a call to `window.fetch` like this:

```javascript
spyOn(window, "fetch")
  .and.returnValue(fetchOkResponse({ /* ... your data here ... */}));
```

Once that’s in place, you’re safe to write a unit test that doesn’t make a real network request. Instead it just returns the stubbed value.

Let’s put that together and look at the first test in `spec/CallbackComponent.spec.js`.

```javascript
import { mount, asSvelteComponent } from "./support/svelte.js";
import CallbackComponent from "../src/CallbackComponent.svelte";

const fetchOkResponse = data =>
  Promise.resolve({ ok: true, json: () => Promise.resolve(data) });

describe(CallbackComponent.name, () => {
  asSvelteComponent();

  beforeEach(() => {
    global.window = {};
    global.window.fetch = () => ({});
    spyOn(window, "fetch")
      .and.returnValue(fetchOkResponse({ price: 99.99 }));
  });

  it("makes a GET request to /price", () => {
    mount(CallbackComponent);
    expect(window.fetch).toHaveBeenCalledWith("/price", { method: "GET" });
  });
});
```

Beyond the set up of the spy, there are a couple more important points to take in:

 1. I’ve set values of `global.window` and `global.window.fetch` _before_ I call `spyOn`. That’s because `spyOn` will complain if the function you’re trying to spy on doesn’t already exist. `window.fetch` does _not_ exist in the Node environment so we need to add it. Another approach is to use [a `fetch` polyfill](https://github.com/github/fetch).
 2. I do not need to use await any promise here. That’s because we don’t care about the _result_ of the call in this test--we only care about the invocation itself.

For the second test, we will need to wait for the promise to complete. Thankfully, Svelte provides a `tick` function we can use for that:

```javascript
import { tick } from "svelte";

it("sets the price when API returned", async () => {
  mount(CallbackComponent);
  await tick();
  await tick();
  expect(container.textContent).toContain("The price is: $99.99");
});
```

### Complex unit tests are telling you something: improve your design!

A problem with this code is that is mixing promise resolution with the rendering of the UI. Although I’ve made these two tests look fairly trivial, in reality there are two ideas in tension: the retrieval of data via a network and the DOM rendering. I much prefer to split all ”business logic” out of my UI components and put it somewhere else.

It’s even questionable if the trigger for pulling data should be the component mounting. Isn’t your application’s workflow driven by something else other than a component? For example, perhaps it’s the page load event, or a user submitting a form? If the trigger isn’t the component, then it doesn’t need an `onMount` call at all.

In the next part, we’ll look at how we can move this logic to a Svelte store, and how we can test components that subscribe to a store.