Testing Svelte async state changes
Heres a short Svelte component that displays the text Submitting...
when a button is clicked:
<script> let submitting = false; const submit = async () => { submitting = true; await window.fetch('/foo'); submitting = false; }</script><button on:click="{submit}" />{#if submitting} Submitting...{/if}
Look carefully at the definition of submit
. The submitting
variable is set to true
before the call to window.fetch
and reset to false
after the call returns.
The text is only rendered when submitting
is true.
In other words, the Submitting...
text appears after the button is clicked and disappears after the window.fetch
call completes.
Why this is difficult to test
This behavior is tricky because one of our tests will need to get into the state where the Submitting...
text is displayed, and freeze in that state while our test runs its expectations. To do that we need to use Sveltes tick
function to ensure the rendered output is updatetd.
Writing the tests
We require three unit tests!
- That the
Submitting...
text appears when the button is clicked. - That initially, no text is displayed.
- That the
Submitting...
text disappears after thewindow.fetch
call completes.
Testing the text appears
Lets take a look at how wed test this.
The test below uses my Svelte testing harness which is just a few dozen lines of code. Ive saved that at spec/svelteTestHarness.js
, and this test exists as spec/Foo.spec.js
.
For more information on how Im running these tests, take a look at my guide to Svelte unit testing.
import expect from "expect";import Foo from "../src/Foo.svelte";import { setDomDocument, mountComponent, click } from "./svelteTestHarness.js";import { tick } from "svelte";describe(Foo.name, () => { beforeEach(setDomDocument); beforeEach(() => { window.fetch = () => Promise.resolve({}); }); it("shows Submitting... when the button is clicked", async () => { mountComponent(Foo); click(container.querySelector("button")); await tick(); expect(container.textContent).toContain("Submitting..."); });});
Notice the use of tick
. Without that, this test wouldnt pass. Thats because when our code executes submitting = true
it doesnt synchronously update the rendered output. Calling tick
tells Svelte to go ahead and perform the update.
Crucially, we havent yet flushed the task queue: calling tick
does not cause the fetch
promise to execute.
In order to make that happen, we need to flush the task queue which well do in the third test.
Testing initial state
First though we have to test the initial state. Without this test, we cant prove that it was the button click that caused the text to appear: it could have been like that from the beginning.
it("initially isnt showing the Submitting text...", async () => { mountComponent(Foo); expect(container.textContent).not.toContain("Submitting...");});
Testing the final state
Finally then, we check what happens after the promise resolves. We need to use await new Promise(setTimeout)
to do this, which flushes the ask queue.
it("hides the Submitting... text when the request promise resolves", async () => { mountComponent(Foo); click(container.querySelector("button")); await new Promise(setTimeout); expect(container.textContent).not.toContain("Submitting...");});
And there it is. Three tests to prove a small piece of behavior. Although it might seem overkill for such a small feature, these tests are quick to writethat is, once you know how to write them
Checkout out my guide to Svelte unit testing for more tips on how to test Svelte.
Original Link: https://dev.to/d_ir/testing-svelte-async-state-changes-3mip
Dev To
An online community for sharing and discovering great ideas, having debates, and making friendsMore About this Source Visit Dev To