sveltejs / svelte

Cybernetically enhanced web apps
https://svelte.dev
MIT License
77.46k stars 4.04k forks source link

[Feature request] Svelte testing library #8287

Open jwerre opened 1 year ago

jwerre commented 1 year ago

Describe the problem

There are a lot of testing libraries out there but none of them work well with Svelte. For example all the the testing methods recommended in the FAQs are not workable solutions since none of them allow the use of slots. Given that Svelte is not a framework but a compiler it needs it's own testing library so we can write tests the Svelte way. It's also extremely important that we're using the same compiler in our tests that we're using in production.

Proposed solution

Tests could be written as Svelte components like this:

button.tests.svelte

<script>

    import {Describe, It, Before, After, expect, userEvent} from '@svelte/test'
    import {Button} from 'my-button-component'
    import {sum} from 'my-utils'

       const clickHandler = (e) => {}

    const buttonTest = (canvas) => {
        const button = canvas.getByRole('button');
        expect(button).toBeInDocument()
        expect(button.innerText).toBe('Yay slots!!')
        userEvent.click(button);
            expect(clickHandler).toHaveBeenCalled();
    }

    const sumTest = (node, args) => {
        const result = sum(args[0], args[1]);
        expect(result).toBe(4);
    }

</script>

<Describe title='Component tests'>

    <Before each={ () => {} } all={ () => {} } />

    <After each={ () => {} } all={ () => {} } />

    <It title='should create a new button component' test={buttonTest} >
        <Button color="green" on:click={clickHandler}>Yay slots!!</Button>
    </It>

</Describe>

<Describe title='Unit tests'>

    <It title='should add two numbers together (Maybe something like this)' use:sumTest={[2, 2]} />

</Describe>

Alternatives considered

svelte-testing-library

Great for handling user events but render function doesn't work with slots and you can't pass data back to parent components.

svelte-csf for Storybook

This is experimental and pretty buggy. Only works is Storybook which is great for component development and not so great for business logic.

svelte-htm

This is an okay solution but we're forced to use components in our tests differently than they're intended to be used and the compiler is not the same one that is used in production. e.g.: render(html`<${Button} on:click=${() => (clicked += 1)}>Click Me!<//>`)

Rik-de-Kort commented 1 year ago

If you lean into the whole "svelte is a language" idea, you could start to take inspiration from things like Pytest, which is a Python testing framework that does rewriting of built-in assert statements to build a test runner. The use is super ergonomic: you just write a normal Python file with functions start with test_ and assert the things you want to check as if you're doing a runtime asserting. Something like this:

from my_button_component import Button

def test_button():
    button = Button(color=green, slots=['yay slots'])
    assert button.inner_html == 'yay slots'

1-to-1 translating that to Svelte:

import {Button} from 'my-button-component'

function test_button_works() {
    const button = render(`<Button color="green">yay slots!</Button>`);
    console.assert(button.innerText === 'yay slots`);
}

I think there are a lot of other design questions that need asking, like how to deal with dispatching events etc. But I think this already reads a lot easier than expect(button.innerText).toBe('yay slots').

jwerre commented 1 year ago

@Rik-de-Kort you can use svelte-htm for rending components as a string. My point is more that we need to be able to test Svelte components in the same way that we use them. This is not about reinventing how testing frameworks work in the javascript/typescript ecosystem. As for assertions, I think it would be nice to be agnostic in this respect. Use whatever you like chai, should etc.... That said, Svelte is for building UI and we need access to the DOM and the ability to mock user events.

Devr-pro commented 1 year ago

This package/feature is more of a SvelteKit feature, testing library itself wrapped in svelte core package wouldn't be ideal and specific thing. (IMO)

jwerre commented 1 year ago

testing library itself wrapped in svelte core package wouldn't be ideal and specific thing.

I agree.

Devr-pro commented 1 year ago

I guess, something like import {Describe, It, Before, After, expect, userEvent} from '@sveltejs/kit/test' could be nice

Radiergummi commented 4 weeks ago

I would like to revitalise this discussion. The testing story around Svelte and SvelteKit in particular is not what it should be: Just having a brief FAQ entry for a massively complex framework like SvelteKit makes it way too hard to establish good testing practices.

Taking a look at the competition:

All of them have a dedicated section on testing, with example code, a selection of frameworks, and general guidelines. I would wish SvelteKit would join them and make testing a first-class citizen, if only in the documentation.

I also really like the original proposal. Svelte-style test files feel great.