Svelte test suite setup
Tags: svelte, javascript, tutorial, testing
Published: 2022-01-27 13:31:27

I love writing with Svelte. It’s pretty simple to learn and you can build quite complex and robust behaviour really quickly. The support and documentation is also awesome: being able to create a basic project with `degit` means you can be productive even faster.

One thing that’s missing from the template project is a starter test suite. This means I’ve never really tried to write any automated tests against my Svelte code. I’ve definitely written large apps that could have done with this level of support. I decided to look into how to do it.

Turns out that it’s actually reasonably easy to get testing set up, although not without its challenges. This brief guide will take you through what worked for me!

# Test runner

Firstly, we need to pick a test runner. Jest is a great choice because it's simple to get running and includes a bunch of useful utilities like mocks and assertions.

```bash
npm install --save-dev jest
```

You'll need to add a couple of extra scripts into your `package.json`. These scripts assume that your source files (and the location of your tests) will be in the `src` folder.

```json
  "scripts": {
    ...
    "test": "jest src",
    "test:watch": "npm run test -- --watch"
  }
```

You should now be able to run the test suite by typing `npm test`. It'll fail, because you have no tests set up, as yet.

# Babel

If you're using ES6 modules (i.e. `import` statements), which you probably are if you're using Svelte, you'll also ned to install Babel to convert into code that's compatible with Jest.

```bash
npm install --save-dev babel @babel/preset-env
```

Create a Babel [config file](https://babeljs.io/docs/en/config-files). I prefer to create a Javascript file `babel.config.js` at the root of the project. NB I tried to use an `.mjs` file, which would theoretically allow ES6 exports, but this appeared incompatible with usage within Jest.

```js
module.exports = {
  presets: [
    [
      '@babel/preset-env',
      { targets: { node: 'current' } },
    ],
  ],
};
```

# Setting up test libraries

We're nearly ready! The last step is to install some test helpers and configure Jest to deal with `.svelte` files.

```bash
npm install --save-dev babel-jest svelte-jester \
  @testing-library/jest-dom @testing-library/svelte
```

Create a `jest.config.mjs` file (this time the ES6 format works!) and add the following configuration:

```js
export default {
  transform: {
    '^.+\\.js$': 'babel-jest',
    '^.+\\.svelte$': 'svelte-jester',
  },
  moduleFileExtensions: ['js', 'svelte'],
  setupFilesAfterEnv: ['<rootDir>/test/jest-setup.js'],
  testEnvironment: 'jsdom',
};
```

Create the file `test/jest-setup.js` and add the following content.

```js
import '@testing-library/jest-dom';
```

This will register the additional assertions from [`@testing-library/jest-dom`](https://github.com/testing-library/jest-dom).

* The `transform` config sets up `babel-jest` to deal with `.js` files and `svelte-jester` to deal with `.svelte` files.
* The `moduleFileExtensions` key registers both `js` and `svelte` files as valid source code for testing.
* `setupFilesAfterEnv` loads the setup file we created.
* Finally, we set the default Jest [test environment](https://jestjs.io/docs/configuration#testenvironment-string) to `jsdom` as we'll primarily be working in code targetted at the browser. As stated in the documentation, it's possible to override this for individual test files by adding a `@jest-environment` docblock at the top.

# Write some tests!

Let's say you have a simple Svelte component called `hello.svelte`.

```html
<script>
  export let name;
</script>

<h1>Hello { name }!</h1>
```

Create a test file in the same location called `hello.test.js`

```js
import { render } from '@testing-library/svelte';
import Hello from './hello';

it('should welcome mum', () => {
  const { getByText } = render(Hello, { name: 'mum' });
  expect(getByText('Hello mum!')).toBeInTheDocument();
});
```

This imports the `render` function from the `@testing-library/svelte` helpers. We use this to instantiate the component, passing in any props as the second argument. In this case, we're setting the `name` prop to `mum`.

The render function returns a object with a series of [helper functions](https://testing-library.com/docs/svelte-testing-library/api). Here, we're only interested in `getByText`, which we destructure from the object.

This searches for elements in the rendered DOM which match the text. In this case, we're looking for an element with the string `Hello mum!`. We wrap this in a Jest expect call, using the `toBeInDocument` matcher that we registered from `jest-dom` to check if the text appears.

# Write some more tests!

That's pretty much it. You now need to make sure you're covering all the critical bits of code that you care about! That's beyond the scope of this post, but here are a couple of links to get you started.

* [How to test Svelte components](https://timdeschryver.dev/blog/how-to-test-svelte-components)
* [Svelte Testing Library](https://testing-library.com/docs/svelte-testing-library/intro)

As a final thought, one of the tricks that Jest has up it's sleeve is built-in test coverage reporting. Run `npm test -- --coverage` to get a report on how much of your code is covered.

You'll notice that you only get coverage stats for code that you're importing into your tests, which means that you could develop a blind spot for modules which you've not yet started.

To fix this, add the following line to the `jest.config.mjs` file.

```js
  collectCoverageFrom: ['<rootDir>/src/**'],
```

You'll now see other `.svelte` and `.js` files listed. Get to work on writing spec for your code! For more details on which code is currently tested, you can open the lcov report that is created for you in `coverage/lcov-report/index.html`.

NB I found that coverage reporting sometimes failed unless I added the following line into the Jest configuration. If you have problems, give that a try.

```js
  testMatch: ['<rootDir>/src/**/*.test.js'],
```

# Thanks

I hope you've found this article useful. Let me know if you have any other hints for testing Svelte code in the comments below.