Unit Testing von Svelte und Vue mit Vitest und Testing Library
Wenhao Wang
Dev Intern · Leapcell

Einleitung
In der schnelllebigen Welt der Frontend-Entwicklung ist die Gewährleistung der Zuverlässigkeit und Wartbarkeit unserer Anwendungen von größter Bedeutung. Da Svelte und Vue mit ihrer eleganten Syntax und ihren Leistungsvorteilen weiter an Bedeutung gewinnen, wird die Notwendigkeit robuster Teststrategien noch kritischer. Insbesondere Unit-Tests dienen als erste Verteidigungslinie, die es Entwicklern ermöglicht, kleine, unabhängige Teile ihrer Codebasis zu isolieren und zu überprüfen. Diese Praxis fängt nicht nur Fehler frühzeitig ab, sondern dient auch als gelebte Dokumentation und fördert das Vertrauen beim Refactoring. Dieser Artikel befasst sich damit, wie wir Vitest, ein blitzschnelles Testframework, zusammen mit Testing Library, einem nutzerzentrierten Test-Utility, effektiv nutzen können, um aussagekräftige Unit-Tests für Svelte- und Vue-Anwendungen zu schreiben. Wir werden das Setup, die Kernprinzipien und praktische Beispiele untersuchen, um Ihr Testspiel zu verbessern.
Kernkonzepte und Werkzeuge für Tests
Bevor wir uns mit den Implementierungsdetails befassen, sollten wir ein gemeinsames Verständnis der beteiligten Schlüsseltechnologien aufbauen:
-
Unit-Testing: Eine Methode der Softwareprüfung, bei der einzelne Einheiten oder Komponenten einer Software getestet werden. Ihr Zweck ist es, zu validieren, dass jede Einheit der Software wie vorgesehen funktioniert. Für Frontend-Frameworks wie Svelte und Vue bezieht sich eine "Einheit" oft auf eine einzelne Komponente, eine Hilfsfunktion oder ein Store-Modul.
-
Vitest: Ein Testframework der nächsten Generation, das auf Vite aufbaut. Es bietet Funktionen wie sofortiges Hot Module Reloading für Tests, native ES-Module-Unterstützung und einen bemerkenswert schnellen Test-Runner. Seine nahtlose Integration mit Vite-basierten Svelte- und Vue-Projekten macht es zur idealen Wahl für modernes Frontend-Testing.
-
Testing Library: Eine Reihe von Utilities, die Ihnen helfen, UI-Komponenten auf nutzerzentrierte Weise zu testen. Ihr Leitprinzip lautet: „Je mehr Ihre Tests dem ähneln, wie Ihre Software verwendet wird, desto mehr Vertrauen können sie Ihnen geben.“ Anstatt mit internen Komponenten (wie State oder Props) direkt zu interagieren, ermutigt es dazu, das DOM abzufragen, wie ein Benutzer es tun würde, und fördert so resilientere und weniger brüchige Tests.
-
Svelte Testing Library / Vue Testing Library: Dies sind Framework-spezifische Wrapper um die Kern-Testing Library, die Utility-Funktionen bereitstellen, die auf Svelte- bzw. Vue-Komponenten zugeschnitten sind. Sie vereinfachen das Mounten von Komponenten und die Interaktion mit ihrer gerenderten Ausgabe.
Einrichtung Ihrer Testumgebung
Lassen Sie uns die anfängliche Einrichtung für Svelte- und Vue-Projekte durchgehen.
Einrichtung einer Svelte-Anwendung
Angenommen, Sie haben ein Svelte-Projekt, das mit Vite initialisiert wurde (z. B. npm create vite@latest my-svelte-app -- --template svelte-ts
), installieren Sie die erforderlichen Testabhängigkeiten:
npm install -D vitest @testing-library/svelte @testing-library/dom jsdom
Konfigurieren Sie als Nächstes vitest
in Ihrer vite.config.js
(oder vite.config.ts
) Datei:
// vite.config.ts import { defineConfig } from 'vite' import { svelte } from '@sveltejs/vite-plugin-svelte' export default defineConfig({ plugins: [svelte()], test: { environment: 'jsdom', // JSDOM für eine browserähnliche Umgebung verwenden globals: true, // Globale APIs wie expect, describe, it verfügbar machen setupFiles: './setupTests.ts', // Optional: für globales Setup, z. B. Erweitern von expect }, })
Erstellen Sie setupTests.ts
(optional, aber bewährte Praxis):
// setupTests.ts import '@testing-library/jest-dom/vitest';
Jetzt sind Sie bereit, Tests zu schreiben.
Einrichtung einer Vue-Anwendung
Ebenso für ein Vue-Projekt, das mit Vite initialisiert wurde (z. B. npm create vue@latest my-vue-app
), installieren Sie die Abhängigkeiten:
npm install -D vitest @vue/test-utils @testing-library/vue @testing-library/dom jsdom
Konfigurieren Sie vitest
in Ihrer vite.config.js
(oder vite.config.ts
):
// vite.config.ts import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' export default defineConfig({ plugins: [vue()], test: { environment: 'jsdom', globals: true, setupFiles: './setupTests.ts', }, })
Erstellen Sie setupTests.ts
(optional):
// setupTests.ts import '@testing-library/jest-dom/vitest';
Mit diesen Setups haben Sie eine robuste Umgebung zum Schreiben von Unit-Tests.
Schreiben von nutzerzentrierten Tests
Die Kernidee hinter Testing Library ist das Testen von Benutzerinteraktionen. Lassen Sie uns praktische Beispiele für Svelte und Vue untersuchen.
Svelte-Beispiel: Eine einfache Zählerkomponente
Betrachten Sie eine einfache Counter.svelte
-Komponente:
<!-- src/components/Counter.svelte --> <script lang="ts"> let count = 0; function increment() { count += 1; } function decrement() { count -= 1; } </script> <div> <p>Count: <span data-testid="count-value">{count}</span></p> <button on:click={decrement}>Decrement</button> <button on:click={increment}>Increment</button> </div>
Schreiben wir nun Counter.test.ts
, um diese Komponente zu testen:
// src/components/Counter.test.ts import { render, screen, fireEvent } from '@testing-library/svelte'; import Counter from './Counter.svelte'; describe('Counter component', () => { it('should display the initial count', () => { render(Counter); const countValue = screen.getByTestId('count-value'); expect(countValue).toHaveTextContent('0'); }); it('should increment the count when the Increment button is clicked', async () => { render(Counter); const incrementButton = screen.getByRole('button', { name: /increment/i }); const countValue = screen.getByTestId('count-value'); await fireEvent.click(incrementButton); // Benutzerklick simulieren expect(countValue).toHaveTextContent('1'); await fireEvent.click(incrementButton); expect(countValue).toHaveTextContent('2'); }); it('should decrement the count when the Decrement button is clicked', async () => { render(Counter); const decrementButton = screen.getByRole('button', { name: /decrement/i }); const countValue = screen.getByTestId('count-value'); // Zuerst inkrementieren, um ein aussagekräftiges Dekrement zu ermöglichen const incrementButton = screen.getByRole('button', { name: /increment/i }); await fireEvent.click(incrementButton); await fireEvent.click(incrementButton); expect(countValue).toHaveTextContent('2'); await fireEvent.click(decrementButton); expect(countValue).toHaveTextContent('1'); await fireEvent.click(decrementButton); expect(countValue).toHaveTextContent('0'); }); });
In diesem Svelte-Beispiel:
render(Counter)
montiert die Komponente in einer virtuellen DOM.screen.getByTestId
undscreen.getByRole
werden verwendet, um Elemente basierend auf ihremdata-testid
-Attribut oder ihrer zugänglichen Rolle und ihrem Namen abzufragen, was simuliert, wie ein Benutzer sie finden würde.fireEvent.click
simuliert das Klicken eines Benutzers auf ein Element.expect(...).toHaveTextContent(...)
ist ein Matcher von@testing-library/jest-dom/vitest
zum Überprüfen des sichtbaren Textinhalts.
Vue-Beispiel: Eine einfache Todo-Listen-Komponente
Betrachten wir nun eine Vue 3-Komponente TodoList.vue
:
<!-- src/components/TodoList.vue --> <template> <div> <h1>Todo List</h1> <input type="text" v-model="newTask" @keyup.enter="addTodo" placeholder="Add a new todo" /> <button @click="addTodo">Add Todo</button> <ul> <li v-for="(todo, index) in todos" :key="index"> {{ todo }} <button @click="removeTodo(index)">Remove</button> </li> </ul> </div> </template> <script lang="ts"> import { defineComponent, ref } from 'vue'; export default defineComponent({ name: 'TodoList', setup() { const newTask = ref(''); const todos = ref<string[]>([]); function addTodo() { if (newTask.value.trim()) { todos.value.push(newTask.value.trim()); newTask.value = ''; } } function removeTodo(index: number) { todos.value.splice(index, 1); } return { newTask, todos, addTodo, removeTodo, }; }, }); </script>
Und der entsprechende Test TodoList.test.ts
:
// src/components/TodoList.test.ts import { render, screen, fireEvent } from '@testing-library/vue'; import TodoList from './TodoList.vue'; describe('TodoList component', () => { it('should display the title', () => { render(TodoList); expect(screen.getByText('Todo List')).toBeInTheDocument(); }); it('should add a new todo when the Add Todo button is clicked', async () => { render(TodoList); const input = screen.getByPlaceholderText('Add a new todo'); const addButton = screen.getByRole('button', { name: /add todo/i }); // In das Eingabefeld tippen await fireEvent.update(input, 'Learn testing'); // Den Button klicken await fireEvent.click(addButton); // Überprüfen, ob die neue Todo angezeigt wird expect(screen.getByText('Learn testing')).toBeInTheDocument(); // Überprüfen, ob das Eingabefeld geleert wurde expect(input).toHaveValue(''); }); it('should remove a todo when its Remove button is clicked', async () => { render(TodoList); const input = screen.getByPlaceholderText('Add a new todo'); const addButton = screen.getByRole('button', { name: /add todo/i }); // Zuerst eine Todo hinzufügen await fireEvent.update(input, 'Buy groceries'); await fireEvent.click(addButton); expect(screen.getByText('Buy groceries')).toBeInTheDocument(); const removeButton = screen.getByRole('button', { name: /remove/i }); // Holt den ersten Remove-Button await fireEvent.click(removeButton); // Überprüfen, ob die Todo nicht mehr im Dokument vorhanden ist expect(screen.queryByText('Buy groceries')).not.toBeInTheDocument(); }); it('should add a new todo when pressing Enter in the input field', async () => { render(TodoList); const input = screen.getByPlaceholderText('Add a new todo'); await fireEvent.update(input, 'Finish blog post'); await fireEvent.keyUp(input, { key: 'Enter', code: 'Enter' }); // Enter-Tastendruck simulieren expect(screen.getByText('Finish blog post')).toBeInTheDocument(); expect(input).toHaveValue(''); }); });
In diesem Vue-Beispiel:
render(TodoList)
montiert die Komponente.screen.getByPlaceholderText
wird verwendet, um das Eingabefeld zu finden.fireEvent.update
simuliert das Tippen in ein Eingabefeld (ändert seinen Wert).fireEvent.keyUp
simuliert einen Tastendruck.expect(...).toBeInTheDocument()
undexpect(...).not.toBeInTheDocument()
werden verwendet, um das Vorhandensein von Elementen zu überprüfen.screen.queryByText
wird verwendet, wenn erwartet wird, dass ein Element nicht vorhanden ist, dagetByText
einen Fehler auslösen würde, wenn es nicht gefunden wird.
Vorteile und Best Practices
Die gemeinsame Nutzung von Vitest und Testing Library bietet erhebliche Vorteile:
- Geschwindigkeit: Vitests HMR und native ESM-Unterstützung führen zu extrem schnellen Testläufen und verbessern die Feedbackschleifen der Entwickler.
- Nutzerzentrierte Tests: Testing Library ermutigt zum Schreiben von Tests, die widerspiegeln, wie Benutzer mit Ihrer UI interagieren, was zu robusteren Tests führt, die aufgrund von Implementierungsänderungen seltener fehlschlagen.
- Barrierefreiheitsbewusstsein: Die Abfrage nach zugänglichen Rollen und Texten fördert den Aufbau zugänglicherer Anwendungen.
- Framework-unabhängige Prinzipien: Obwohl die spezifischen
render
-Funktionen variieren, sind die Kern-Query- und Interaktionsmuster von Testing Library über Svelte, Vue, React und Angular hinweg konsistent, was den Wechsel des Frameworks oder die Wartung mehrerer Projekte erleichtert.
Best Practices:
- Testen Sie das Verhalten, nicht die Implementierung: Konzentrieren Sie sich darauf, was die Komponente tut und wie der Benutzer sie wahrnimmt, nicht auf ihren internen Zustand oder Methodenaufrufe.
- Verwenden Sie zugängliche Abfragen: Bevorzugen Sie
getByRole
,getByLabelText
,getByPlaceholderText
,getByText
. Verwenden SiegetByTestId
als letzte Option, wenn keine andere zugängliche Abfrage geeignet ist. - Bereinigen Sie nach den Tests: Obwohl Testing Library dies oft handhabt, stellen Sie sicher, dass kein globaler Zustand zwischen den Tests durchsickert.
- Halten Sie Tests klein und fokussiert: Jeder Test sollte idealerweise nur ein spezifisches Verhalten überprüfen.
- Führen Sie Tests häufig aus: Integrieren Sie das Testen in Ihren täglichen Arbeitsablauf und möglicherweise in Ihre CI/CD-Pipeline.
Fazit
Die Übernahme von Vitest und Testing Library für Ihre Svelte- oder Vue-Projekte bietet einen leistungsstarken und pragmatischen Ansatz für Unit-Testing. Indem Sie sich auf nutzerzentrierte Interaktionen konzentrieren und einen modernen, schnellen Test-Runner nutzen, können Sie Tests schreiben, die zuverlässiger und leichter zu warten sind und mehr Vertrauen in die Qualität Ihrer Anwendung schaffen. Diese Kombination optimiert Ihren Entwicklungsprozess und stellt sicher, dass Ihre Svelte- und Vue-Anwendungen nicht nur gut funktionieren, sondern auch dem realen Einsatz standhalten. Nutzen Sie diese Werkzeuge, um robustere und benutzerfreundlichere Frontend-Erlebnisse zu schaffen.