Search icon CANCEL
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Conferences
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
Svelte with Test-Driven Development

You're reading from   Svelte with Test-Driven Development Advance your skills and write effective automated tests with Vitest, Playwright, and Cucumber.js

Arrow left icon
Product type Paperback
Published in Jun 2023
Publisher Packt
ISBN-13 9781837638338
Length 250 pages
Edition 1st Edition
Languages
Arrow right icon
Author (1):
Arrow left icon
Daniel Irvine Daniel Irvine
Author Profile Icon Daniel Irvine
Daniel Irvine
Arrow right icon
View More author details
Toc

Table of Contents (22) Chapters Close

Preface 1. Part 1: Learning the TDD Cycle
2. Chapter 1: Setting up for Testing FREE CHAPTER 3. Chapter 2: Introducing the Red-Green-Refactor Workflow 4. Chapter 3: Loading Data into a Route 5. Chapter 4: Saving Form Data 6. Chapter 5: Validating Form Data 7. Chapter 6: Editing Form Data 8. Part 2: Refactoring Tests and Application Code
9. Chapter 7: Tidying up Test Suites 10. Chapter 8: Creating Matchers to Simplify Tests 11. Chapter 9: Extracting Logic Out of the Framework 12. Chapter 10: Test-Driving API Endpoints 13. Chapter 11: Replacing Behavior with a Side-By-Side Implementation 14. Chapter 12: Using Component Mocks to Clarify Tests 15. Chapter 13: Adding Cucumber Tests 16. Part 3: Testing SvelteKit Features
17. Chapter 14: Testing Authentication 18. Chapter 15: Test-Driving Svelte Stores 19. Chapter 16: Test-Driving Service Workers 20. Index 21. Other Books You May Enjoy

Configuring support for Svelte component tests

A Svelte component test is one that, perhaps unsurprisingly, tests a Svelte component. For this, we need access to a Document Object Model (DOM), which isn’t part of the standard Node.js environment. We’ll also need some additional packages for writing unit test expectations against the DOM.

Installing jsdom and testing library helpers

At the Terminal, run the following command to install the jsdom package and @testing-library packages that we’ll use in our unit tests:

npm install --save-dev \
  jsdom \
  @testing-library/svelte \
  @testing-library/jest-dom \
  @testing-library/user-event

If you’re using TypeScript, at this point, you may wish to add packages containing type definitions.

Next, create a new file named src/vitest/registerMatchers.js with the following content. It ensures that the matchers we’ll be using are available for use via the expect function:

import matchers from '@testing-library/jest-dom/matchers';
import { expect } from 'vitest';
expect.extend(matchers);

Then, update vite.config.js to add a new environment property, which installs jsdom correctly, and also a setupFiles property, which ensures the file defined previously is loaded (and invoked) just before the test suites are loaded:

 const config = {
   plugins: [sveltekit()],
   test: {
     ...,
     reporter: 'verbose',
     environment: 'jsdom',
     setupFiles: ['./src/vitest/registerMatchers.js']
   }
 };

That’s it for the basic setup. Now let’s test it out.

Writing a test for the DOM

Open the src/index.test.js file and add the following test definition, inside the describe block. This test makes use of the document object that is created for us by the jsdom package, and the toHaveTextContent matcher that is provided by the @testing-library/jest-dom package:

it('renders hello into the document', () => {
  document.body.innerHTML =
    '<h1>Hello, world!</h1>';
  expect(document.body).toHaveTextContent(
    'Hello, world!'
  );
});

Now, if you run the test, you should see it pass. But, just as you did with the first test, it’s important to confirm the test actually tests what it says it does. Change the test by commenting out or deleting the first line of the test, and then re-running the test runner.

You should see an output as follows:

 FAIL  src/index.test.js > sum test > renders hello into the document
Error: expect(element).toHaveTextContent()
Expected element to have text content:
  Hello, world!
Received:
  src/index.test.js:9:25
      7|
      8|  it('renders hello into the document', () => {
      9|   expect(document.body).toHaveTextContent(
       |                         ^
     10|    'Hello, world!'
     11|   );

That proves the test is working. You can go ahead and undo the breaking change you made.

Writing a first Svelte component test

Next, let’s write an actual Svelte component and test that out. Create a new file named src/Hello.svelte with the following content:

<script>
  export let name;
</script>
<p>Hello, {name}!</p>

Then, go back to the src/index.test.js file and refactor your test to use this new component. To do that, replace the call to document.outerHTML with a call to the render function, like this:

it('renders hello into the document', () => {
  render(Hello, { name: 'world' });
  expect(document.body).toHaveTextContent(
    'Hello, world!'
  );
});

This render function comes from the @testing-library/svelte package. Import that now, along with an import for the Hello component, placed at the top of the file:

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

Check that the test still passes with the refactor.

Then, add this third test, which verifies that the name prop in the component is being used to verify the output:

it('renders hello, svelte', () => {
  render(Hello, { name: 'Svelte' });
  expect(document.body).toHaveTextContent(
    'Hello, Svelte!'
  );
});

Run the test and make sure it passes.

Now, go ahead and comment out the render call in the last test. You might think that the test fails with an error saying nothing was rendered on-screen. But let’s see what happens:

Error: expect(element).toHaveTextContent()
Expected element to have text content:
  Hello, Svelte!
Received:
  Hello, world!

Hold on a second; is this what we expected? This test didn’t ever print out a Hello, world! message so why is the test expectation picking it up?

It turns out that our tests share the same document object, which is clearly not good for test independence. Imagine if the second test also expected to see Hello, world! rather than Hello, Svelte!. It would have passed by virtue of the first test running. We need to do something about this.

Ensuring the DOM is cleared after each test run

We want to make sure that every test gets its own clean version of the DOM. We can do this by using the cleanup function.

Create a new file named src/vitest/cleanupDom.js:

import { afterEach } from 'vitest';
import { cleanup } from '@testing-library/svelte';
afterEach(cleanup);

Then, insert that into the setupFiles property in vite.config.js:

const config = {
  ...,
  test: {
    ...,
    setupFiles: [
      './src/vitest/cleanupDom.js',
      './src/vitest/registerMatchers.js'
    ]
  }
};

Now, if you run your failing test again, you should see that the Hello, world! message no longer appears.

Before continuing, uncomment the render call and check your tests are back in an all-green state.

Restoring mocks automatically

There’s one final piece of configuration we need in vite.config.js. Add the restoreMocks property, as shown here:

const config = {
  ...,
  test: {
    ...,
    restoreMocks: true
  }
};

This is also important for test independence and will be important in Chapter 11, Replacing Behavior with a Side-By-Side Implementation, when we begin using the vi.fn function for building test doubles.

That covers all the configuration you need for the rest of the book. The next section touches briefly on some optional configurations you might want to consider.

You have been reading a chapter from
Svelte with Test-Driven Development
Published in: Jun 2023
Publisher: Packt
ISBN-13: 9781837638338
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $19.99/month. Cancel anytime