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 :
npminstall-Dvitest
Puis ajuster votre fichier vite.config.js :
vite.config
import{defineConfig(alias)functiondefineConfig(config:UserConfig):UserConfig(+4overloads)importdefineConfig}from'vitest/config';exportdefaultdefineConfig(alias)defineConfig(config:UserConfig):UserConfig(+4overloads)importdefineConfig({ // ...
// Dit à Vitest d'utiliser les points d'entrée `browser` dans les fichiers `package.json`, même si // Vitest utilise Node
resolve(property)resolve?:AllResolveOptions|undefined:processvarprocess:NodeJS.Process.env(property)NodeJS.Process.env:NodeJS.ProcessEnv
The process.env property returns an object containing the user environment.
See environ(7).
It is possible to modify this object, but such modifications will not be
reflected outside the Node.js process, or (unless explicitly requested)
to other Worker threads.
In other words, the following example would not work:
Assigning a property on process.env will implicitly convert the value
to a string. This behavior is deprecated. Future versions of Node.js may
throw an error when the value is not a string, number, or boolean.
Unless explicitly specified when creating a Worker instance,
each Worker thread has its own copy of process.env, based on its
parent thread's process.env, or whatever was specified as the env option
to the Worker constructor. Changes to process.env will not be visible
across Worker threads, and only the main thread can make changes that
are visible to the operating system or to native add-ons. On Windows, a copy of process.env on a Worker instance operates in a case-sensitive manner
unlike the main thread.
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 :
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',()=>{constcleanup=$effect.root(()=>{letcount=$state(0);// logger utilise un $effect pour afficher les mises à jour de son inputletlog=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 synchroneflushSync();expect(log).toEqual([0]);count=1;flushSync();expect(log).toEqual([0,1]);});cleanup();});
Runs code when a component is mounted to the DOM, and then whenever its dependencies change, i.e. $state or $derived values.
The timing of the execution is after the DOM has been updated.
Example:
$effect(()=>console.log('The count is now '+count));
If you return a function from the effect, it will be called right before the effect is run again, or when the component is unmounted.
Runs code when a component is mounted to the DOM, and then whenever its dependencies change, i.e. $state or $derived values.
The timing of the execution is after the DOM has been updated.
Example:
$effect(()=>console.log('The count is now '+count));
If you return a function from the effect, it will be called right before the effect is run again, or when the component is unmounted.
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) :
:{// 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(property)InlineConfig.environment?:VitestEnvironment|undefined
If used unsupported string, will try to load the package vitest-environment-${env}
'node'
:'jsdom'},// Dit à Vitest d'utiliser les points d'entrée `browser` dans les fichiers `package.json`, même si // Vitest utilise Node
resolve(property)resolve?:AllResolveOptions|undefined:processvarprocess:NodeJS.Process.env(property)NodeJS.Process.env:NodeJS.ProcessEnv
The process.env property returns an object containing the user environment.
See environ(7).
It is possible to modify this object, but such modifications will not be
reflected outside the Node.js process, or (unless explicitly requested)
to other Worker threads.
In other words, the following example would not work:
Assigning a property on process.env will implicitly convert the value
to a string. This behavior is deprecated. Future versions of Node.js may
throw an error when the value is not a string, number, or boolean.
Unless explicitly specified when creating a Worker instance,
each Worker thread has its own copy of process.env, based on its
parent thread's process.env, or whatever was specified as the env option
to the Worker constructor. Changes to process.env will not be visible
across Worker threads, and only the main thread can make changes that
are visible to the operating system or to native add-ons. On Windows, a copy of process.env on a Worker instance operates in a case-sensitive manner
unlike the main thread.
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 :
Mounts a component to the given target and returns the exports and potentially the props (if compiled with accessors: true) of the component.
Transitions will play during the initial render unless the intro option is set to false.
Defines a test case with a given name and test function. The test function can optionally be configured with test options.
name
The name of the test or a function that will be used as a test name.
optionsOrFn
Optional. The test options or the test function if no explicit name is provided.
optionsOrTest
Optional. The test function or options, depending on the previous parameters.
{Error} If called inside another test function.
// Define a simple testtest('should add two numbers',()=>{expect(add(1,2)).toBe(3);});
// Define a test with optionstest('should subtract two numbers',{retry:3},()=>{expect(subtract(5,2)).toBe(3);});
('Component',()=>{ // Instantier le composant en utilisant l'API Svelte `mount`
constcomponentconstcomponent:{$on?(type:string,callback:(e:any)=>void):()=>void;$set?(props:Partial<Record<string,any>>):void;}&Record<string,any>=mount(alias)mount<Record<string,any>,{$on?(type:string,callback:(e:any)=>void):()=>void;$set?(props:Partial<Record<string,any>>):void;}&Record<string,any>>(component:ComponentType<SvelteComponent<Record<string,any>,any,any>>|Component<Record<string,any>,{$on?(type:string,callback:(e:any)=>void):()=>void;$set?(props:Partial<Record<string,any>>):void;}&Record<string,any>,any>,options:MountOptions<...>):{$on?(type:string,callback:(e:any)=>void):()=>void;$set?(props:Partial<Record<string,any>>):void;}&Record<...>importmount
Mounts a component to the given target and returns the exports and potentially the props (if compiled with accessors: true) of the component.
Transitions will play during the initial render unless the intro option is set to false.
The innerHTML property of the Element interface gets or sets the HTML or XML markup contained within the element, omitting any shadow roots in both cases.
('<button>0</button>');// Clic sur le bouton, puis synchronisation des changements pour définir les attentes de manière // synchrone
documentvardocument:Document
window.document returns a reference to the document contained in the window.
The HTMLElement.click() method simulates a mouse click on an element. When called on an element, the element's click event is fired (unless its disabled attribute is set).
The innerHTML property of the Element interface gets or sets the HTML or XML markup contained within the element, omitting any shadow roots in both cases.
('<button>1</button>'); // Suppression du composant du DOM
unmount(alias)unmount(component:Record<string,any>,options?:{outro?:boolean;}|undefined):Promise<void>importunmount
Unmounts a component that was previously mounted using mount or hydrate.
Since 5.13.0, if options.outro is true, transitions will play before the component is removed from the DOM.
Returns a Promise that resolves after transitions have completed if options.outro is true, or immediately otherwise (prior to 5.13.0, returns void).
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 :
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
<scriptmodule>import{defineMeta}from'@storybook/addon-svelte-csf';import{expect,fn}from'storybook/test';importLoginFormfrom'./LoginForm.svelte';const{Story}=defineMeta({component:LoginForm,args:{// On passe une fonction de mock à la prop `onSubmit`onSubmit:fn(),}});</script><Storyname="Formulaire vide"/><Storyname="Formulaire rempli"play={async({args,canvas,userEvent})=>{// On simule un utilisateur qui remplir le formulaireawaituserEvent.type(canvas.getByTestId('email'),'email@provider.com');awaituserEvent.type(canvas.getByTestId('password'),'a-random-password');awaituserEvent.click(canvas.getByRole('button'));// On vérifielesassertionsawaitexpect(args.onSubmit).toHaveBeenCalledTimes(1);awaitexpect(canvas.getByText('You’rein!')).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
constconfigconstconfig:{webServer:{command:string;port:number;};testDir:string;testMatch:RegExp;}={webServer(property)webServer:{command:string;port:number;}:{command(property)command:string:'npm run build && npm run preview',port(property)port:number:4173},testDir(property)testDir:string:'tests',testMatch(property)testMatch:RegExp:/(.+\.)?(test|spec)\.[jt]s/};exportdefaultconfigconstconfig:{webServer:{command:string;port:number;};testDir:string;testMatch:RegExp;};
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{expectimportexpect,testimporttest}from'@playwright/test';testimporttest("la page d'accueil a le h1 prévu",async({page(parameter)page:any})=>{awaitpage(parameter)page:any.gotoany('/');awaitexpectimportexpect(page(parameter)page:any.locatorany('h1')).toBeVisibleany();});