Skip to main content

Tests

Les tests vous aident à écrire et maintenir votre code et vous protègent contre les régressions. Les frameworks de test vous aident à ça, vous permettant de décrire des assertions ou des attentes sur le comportement de votre code. Svelte ne vous oriente pas sur tel ou tel framework à utiliser — vous pouvez écrire des tests unitaires, des tests d’intégration, des tests end-to-end en utilisant des solutions comme Vitest, Jasmine, Cypress et Playwright

Tests unitaires et de composants avec Vitest

Les tests unitaires vous permettent de tester des petites parties isolées de votre code. Les tests d’intégration vous permettent de vérifier comment des morceaux de votre application fonctionnent ensemble. Si vous utilisez Vite (notamment via SvelteKit), nous vous recommandons d’utiliser Vitest. Vous pouvez utiliser le CLI de Svelte pour mettre en place Vitest soit lors de la création du projet, soit plus tard.

Pour mettre en place Vitest manuellement, commencez par installer Vitest :

npm install -D vitest

Puis ajuster votre fichier vite.config.js :

vite.config
import { defineConfig } from 'vitest/config';

export default defineConfig({
	// ...
	// Dit à Vitest d'utiliser les points d'entrée `browser` dans les fichiers `package.json`, même si
	// Vitest utilise Node
	resolve: process.env.VITEST
		? {
				conditions: ['browser']
			}
		: undefined
});

Si charger la version navigateur de tous vos paquets n’est pas possible, parce que vous testez également des librairies backend, vous pourriez avoir également besoin d’une configuration d’alias

Vous pouvez maintenant écrire vos tests unitaires dans des fichiers .js/.ts :

multiplier.svelte.test
import { flushSync } from 'svelte';
import { expect, test } from 'vitest';
import { multiplier } from './multiplier.svelte.js';

test('Multiplier', () => {
	let double = multiplier(0, 2);

	expect(double.value).toEqual(0);

	double.set(5);

	expect(double.value).toEqual(10);
});
multiplier.svelte
/**
 * @param {number} initial
 * @param {number} k
 */
export function multiplier(initial, k) {
	let count = $state(initial);

	return {
		get value() {
			return count * k;
		},
		/** @param {number} c */
		set: (c) => {
			count = c;
		}
	};
}
export function multiplier(initial: number, k: number) {
	let count = $state(initial);

	return {
		get value() {
			return count * k;
		},

		set: (c: number) => {
			count = c;
		}
	};
}

Utiliser les runes dans vos fichiers de test

Puisque Vitest traite vos fichiers de test de la même façon que vos fichiers source, vous pouvez utiliser des runes au sein de vos tests tant que leur nom de fichier inclut .svelte :

multiplier.svelte.test
import { flushSync } from 'svelte';
import { expect, test } from 'vitest';
import { multiplier } from './multiplier.svelte.js';

test('Multiplier', () => {
	let count = $state(0);
	let double = multiplier(() => count, 2);

	expect(double.value).toEqual(0);

	count = 5;

	expect(double.value).toEqual(10);
});
multiplier.svelte
/**
 * @param {() => number} getCount
 * @param {number} k
 */
export function multiplier(getCount, k) {
	return {
		get value() {
			return getCount() * k;
		}
	};
}
export function multiplier(getCount: () => number, k: number) {
	return {
		get value() {
			return getCount() * k;
		}
	};
}

Si le code testé utilise des effets, vous aurez besoin de placer le test dans un $effect.root :

logger.svelte.test
import { flushSync } from 'svelte';
import { expect, test } from 'vitest';
import { logger } from './logger.svelte.js';

test('Effet', () => {
	const cleanup = $effect.root(() => {
		let count = $state(0);

		// logger utilise un $effect pour afficher les mises à jour de son input
		let log = logger(() => count);

		// les effets sont en général exécutés après une micro-tâche,
		// utiliser `flushSync` pour exécuter tous les effets en attente de manière synchrone
		flushSync();
		expect(log).toEqual([0]);

		count = 1;
		flushSync();

		expect(log).toEqual([0, 1]);
	});

	cleanup();
});
logger.svelte
/**
 * @param {() => any} getValue
 */
export function logger(getValue) {
	/** @type {any[]} */
	let log = [];

	$effect(() => {
		log.push(getValue());
	});

	return log;
}
export function logger(getValue: () => any) {
	let log: any[] = [];

	$effect(() => {
		log.push(getValue());
	});

	return log;
}

Tests de composants

Il est possible de tester vos composants en isolation, ce qui vous permet de les rendre dans un navigateur (réel ou simulé), de simuler leur comportement, et faire des assertions, sans démarrer toute votre application.

Avant d’écrire des tests de composant, posez-vous la question de si vous avez réellement besoin de tester le composant, ou si vous souhaitez plutôt tester la logique au sein de votre composant. Si c’est le cas, envisagez d’extraire cette logique afin de pouvoir la tester indépendamment du composant.

Pour commencer, installer jsdom (une librairie qui simule les APIs du DOM) :

npm install -D jsdom

Puis ajustez votre vite.config.js :

vite.config
import { defineConfig } from 'vitest/config';

export default defineConfig({
	plugins: [
		/* ... */
	],
	test: {
		// Si vous testez des composants côté client, vous aurez besoin de mettre en place un
		// environnement DOM. Si tous vos fichiers ne sont pas compatibles avec cet environnement, vous
		// pouvez plutôt ajouter un commentaire `// @vitest-environment jsdom` en haut des fichiers de
		// test.
		environment: 'jsdom'
	},

	// Dit à Vitest d'utiliser les points d'entrée `browser` dans les fichiers `package.json`, même si
	// Vitest utilise Node
	resolve: process.env.VITEST
		? {
				conditions: ['browser']
			}
		: undefined
});

Vous pouvez ensuite créer un fichier de test dans lequel importer le composant à tester, interagir avec lui programmatiquement et définir les résultats attendus :

component.test
import { flushSync, mount, unmount } from 'svelte';
import { expect, test } from 'vitest';
import Component from './Component.svelte';

test('Component', () => {
	// Instantier le composant en utilisant l'API Svelte `mount`
	const component = mount(Component, {
		target: document.body, // `document` existe grâce à jsdom
		props: { initial: 0 }
	});

	expect(document.body.innerHTML).toBe('<button>0</button>');

	// Clic sur le bouton, puis synchronisation des changements pour définir les attentes de manière
	// synchrone
	document.body.querySelector('button')?.click();
	flushSync();

	expect(document.body.innerHTML).toBe('<button>1</button>');

	// Suppression du composant du DOM
	unmount(component);
});

Même le processus est plutôt simple à mettre en place, celui-ci est aussi bas niveau et plutôt fragile, puisque la structure de composant peut beaucoup évoluer. Des outils comme @testing-library/svelte peuvent aider à industrialiser l’écriture de vos tests. Le test ci-dessus peut ainsi être ré-écrit comme ceci :

component.test
import { render, screen } from '@testing-library/svelte';
import userEvent from '@testing-library/user-event';
import { expect, test } from 'vitest';
import Component from './Component.svelte';

test('Component', async () => {
	const user = userEvent.setup();
	render(Component);

	const button = screen.getByRole('button');
	expect(button).toHaveTextContent(0);

	await user.click(button);
	expect(button).toHaveTextContent(1);
});

Lorsque vous écrivez des tests de composant qui impliquent des liaisons à double sens, du contexte ou des props de snippet, il est recommandé de créer un composant parent spécifiquement pour votre test, et interagir avec ce composant. @testing-library/svelte montre quelques exemples de cette technique.

Tests de composant avec Storybook

Storybook est un outil permettant de développer et de documenter des composants d’interface, et peut également être utilisé pour tester vos composants. Les tests sont lancés avec le mode ‘browser’ de Vitest, qui rend vos composants dans un vrai navigateur pour obtenir un environnement de test le plus réaliste possible.

Tout d’abord, installez Storybook (en utilisant l’outil CLI de Svelte) dans votre projet via npx sv add storybooK et choisissez la configuration recommandée incluant les fonctionnalités de test. Si vous utilisez déjà Storybook, et pour plus d’information sur les fonctionnalités de test de Storybook, lisez la documentation de test de Storybook.

Vous pouvez créer des stories montrant les variations de composant et tester les interactions avec la fonction play, qui vous permet de simuler le comportement et de faire des assertions en utilisant Testing Library et les API Vitest. Voici un exemple de deux stories pouvant être testées, une qui rend un composant LoginForm vide et une qui simule un utilisateur remplissant le formulaire :

LoginForm.stories
<script module>
	import { defineMeta } from '@storybook/addon-svelte-csf';
	import { expect, fn } from 'storybook/test';

	import LoginForm from './LoginForm.svelte';

	const { Story } = defineMeta({
		component: LoginForm,
		args: {
			// On passe une fonction de mock à la prop `onSubmit`
			onSubmit: fn(),
		}
	});
</script>

<Story name="Formulaire vide" />

<Story
	name="Formulaire rempli"
	play={async ({ args, canvas, userEvent }) => {
		// On simule un utilisateur qui remplir le formulaire
		await userEvent.type(canvas.getByTestId('email'), 'email@provider.com');
		await userEvent.type(canvas.getByTestId('password'), 'a-random-password');
		await userEvent.click(canvas.getByRole('button'));

		// On vérifie les assertions
		await expect(args.onSubmit).toHaveBeenCalledTimes(1);
		await expect(canvas.getByText('You’re in!')).toBeInTheDocument();
	}}
/>

Tests end-to-end avec Playwright

Les tests E2E (“end to end” en anglais, qui se traduit par “de bout en bout”) vous permettent de tester votre application toute entière depuis le point de vue de vos utilisateurs. Cette section prend Playwright comme exemple, mais vous pouvez aussi utiliser d’autres solutions comme Cypress ou NightwatchJS.

Vous pouvez utiliser le CLI de Svelte pour mettre en place Playwright soit pendant la création du projet, soit plus tard. Vous pouvez aussi le mettre en place avec npm init playwright. De plus, vous pourriez également vouloir installer un plugin d’IDE tel que l’extension VSCode pour être capable d’exécuter des tests depuis votre IDE.

Si vous avez lancé npm init playwright ou n’utilisez pas Vite, vous pourriez avoir besoin d’ajuster la configuration de Playwright pour lui dire quoi faire avant de lancer les tests — principalement lancer votre application sur un certain port. Par exemple :

playwright.config
const config = {
	webServer: {
		command: 'npm run build && npm run preview',
		port: 4173
	},
	testDir: 'tests',
	testMatch: /(.+\.)?(test|spec)\.[jt]s/
};

export default config;

Vous pouvez maintenant commencer à écrire vos tests. Ils n’ont aucune conscience que Svelte existe en tant que framework, il vous faudra donc surtout interagir avec le DOM, et écrire vos assertions.

tests/hello-world.spec
import { expect, test } from '@playwright/test';

test("la page d'accueil a le h1 prévu", async ({ page }) => {
	await page.goto('/');
	await expect(page.locator('h1')).toBeVisible();
});

Modifier cette page sur Github llms.txt

précédent suivant