Testing Svelte stores and mocking dependencies
Tags: svelte, testing, tdd, series
Published: 2020-01-12 14:04:57

---
title: Testing Svelte stores and mocking dependencies
published: true
description: Use Svelte stores to keep your components neat and tidy
tags: svelte, testing, tdd, series
series: Writing unit tests for Svelte
---

We’ve covered off the basics and it’s time to get to the fun stuff.

In this part of the series, we’ll move the `fetch` call from the `CallbackComponent` into a module that updates a Svelte store.

Then we’ll use [babel-plugin-rewire-exports](https://github.com/asapach/babel-plugin-rewire-exports) to spy on our new store fetch call and check that `onMount` calls it when the component mounts. 

Finally, we’ll send a store update to verify that our component is in fact responding to updates.

Refer to the GitHub repository for all of the code samples.

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

### Extracting a `price` store from `CallbackComponent`

The file `src/stores/price.js` is as follows.

```javascript
import { writable } from "svelte/store";

export const price = writable("");

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

Now `src/CallbackComponent.js` can be updated to use that store:

```html
<script>
  import { onMount } from "svelte";
  import { fetch as fetchPrice, price } from "./stores/price.js";

  onMount(fetchPrice);
</script>

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

Here I’m using the auto-subscribe feature of Svelte. By referring to `price` using the `$` prefix, i.e. `$price`, Svelte takes care of the subscription _and unsubscription_ for me. I like that. 👏

This code is looking much better already, and if you run `npm test spec/CallbackComponent.spec.js` you’ll find the existing tests still work. In fact, some people might say that you can leave this here and not bother with refactoring the test. Well, not me, I don’t say that: I say we need to get things in order before some later time in the future when we add to `price` and our `CallbackComponent` tests break unexpectedly.

(By the way, in the last part I mentioned that the *trigger* for the price fetch may be better placed elsewhere, not in a Svelte component, but let’s go with this approach for now as it allows me to neatly introduce mocks.)

### Testing the store

The tests, in `spec/stores/price.spec.js`, look like this:

```javascript
import { tick } from "svelte";
import { get } from "svelte/store";
import { fetch, price } from "../../src/stores/price.js";

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

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

    price.set("");
  });

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

  it("sets the price when API returned", async () => {
    fetch();
    await tick();
    await tick();
    expect(get(price)).toEqual(99.99);
  });
});
```

This is all _very_ similar to the previous tests for `CallbackComponent`. The changes are:

 * The call to `mount(CallbackComponent)` is replaced with a call to `fetch()`
 * Rather than checking that the price is rendered in the DOM, we check that the value of the store is `99.99`. To do that, we use Svelte's `get` function.
 * Crucially, we have to reset the store in between tests. I do that by calling `price.set("");` in the `beforeEach block`.

### Resetting store state between tests: a better approach

The problem with the above approach is that the initial store value, `""`, is stored in two places: once in `src/stores/price.js` and once in its tests.

To fix that, we can create a `reset` function in `src/stores/price.js`:

```javascript
const initialValue = "";

export const reset = () => price.set(initialValue);

export const price = writable(initialValue);
```

Now we can use `reset` inside of the tests:

```javascript
import { fetch, price, reset as resetPrice } from "../../src/stores/price.js";
...

describe(fetch.name, () => {
  beforeEach(() => {
    ...
    resetPrice();
  });
});
```

You may have noticed that I’m quite fond of renaming imports--`price` exports `fetch` and `reset` functions, but I’ve renamed them as `fetchPrice` and `resetPrice`, for clarity.

### Rewriting the `CallbackComponent` specs

Let’s start with the easiest spec: we want to check that the component subscribes to updates, so we should mount the component and _afterwards_ update the price of the component.

Any time a state change occurs in a component, I like to ensure I have _two_ tests: one for the initial value, and one for the changed value.

In the next code sample, I’ve purposefully made life hard for us just to make a point. Since our code is still calling the _real_ `fetch` function in `src/stores/price.js`, we _still_ need to stub out `window.fetch`. If we don’t, our tests will error. (Try it if you don’t believe me!)

```javascript
import { tick } from "svelte";
import { mount, asSvelteComponent } from "./support/svelte.js";
import { price } from "../src/stores/price.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("displays the initial price", () => {
    price.set(99.99);
    mount(CallbackComponent);
    expect(container.textContent).toContain("The price is: $99.99");
  });

  it("updates when the price changes", async () => {
    mount(CallbackComponent);
    price.set(123.45);
    await tick();
    expect(container.textContent).toContain("The price is: $123.45");
  });
});
```

Concentrating on the two tests for a moment, you can see I’m using `set` to set the price. In the second test, I do that after the component is mounted to ensure that the subscription is working correctly.

(I didn’t bother to write a test for the _un_subscribe behavior. If I chose to implement the production code with `subscribe` instead, my test would still pass. But if I didn’t remember to unsubscribe, there’d be a memory leak in my component. Everyone has their limit to how strict they’ll be with their testing... I guess that’s mine!)

But what about all that irrelevant stub set up? The moment has finally arrived: it’s time to _rewire_ our `fetch` function.

### Replacing dependencies with `rewire`

When we stubbed out `window.fetch`, we were lucky in that it was a global function. But now we have a dependency that we want to stub out which is a module export: `fetch`, the export from `src/stores/price.js`.

To stub that, we need to rewire the export.

I played around with a few different packages, including [rollup-plugin-stub](https://github.com/eventualbuddha/rollup-plugin-stub) which would be ideal _except_ the package has been archived (I don’t know why) and the interface isn’t as nice as the actual choice I’ve gone with, which is [babel-plugin-rewire-exports](https://github.com/asapach/babel-plugin-rewire-exports).

_(If you're interested in using rollup-plugin-stub, I suggest using [my fork](https://github.com/dirv/rollup-plugin-stub) which has a couple of improvements.)_

So first things first, this is a Babel plugin so we have to tell Rollup to load Babel. You need to install all of the following packages, all as dev dependencies:

`@babel/core`
`rollup-plugin-babel`
`babel-plugin-rewire-exports`

Then it gets enabled in `rollup.test.config.js`:

```javascript
import babel from "rollup-plugin-babel";

export default {
  ...
  plugins: [
    ...,
    babel({
      extensions: [".js", ".svelte"],
      plugins: ["rewire-exports"]
    })
  ]
};
```

No Babel config is required--that’s all you need!

Let’s get down to business. We’re going to use `jasmine.createSpy` to create a spy, `rewire$fetch` to rewire the fetch call, and `restore` to restore all the original function.

First up, the import for `src/stores/price.js` changes to the following.

```javascript
import {
  price,
  fetch as fetchPrice,
  rewire$fetch,
  restore
} from "../src/stores/price.js";
```

It’s the rewire plugin that provides the `rewire$fetch` and `restore` function. We’ll see how to use both in the next example. The most complex part is that `rewire$fetch` operates only on `fetch`, but `restore` will restore _all_ of the mocks from that module.

And then, the test:

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

  beforeEach(() => {
    rewire$fetch(jasmine.createSpy());
  });

  afterEach(() => {
    restore();
  });

  // ... previous two tests ...

  it("fetches prices on mount", () => {
    mount(CallbackComponent);
    expect(fetchPrice).toHaveBeenCalled();
  });
});
```

The test itself is very simple, which is great. And we can delete the existing stubbing of `window.fetch`, which is good because the component doesn’t make any reference to `window.fetch`.

In the next part we’ll continue our use of mocking but extend it to mocking components themselves, for which we’ll need a [component double](https://github.com/dirv/svelte-component-double).