Pourquoi Tester Vue avec Jest ?
Dans un environnement de développement front-end rapide et dynamique, tester votre application est essentiel pour assurer sa qualité et sa fiabilité. Jest est une bibliothèque de tests JavaScript populaire qui permet de vérifier l'intégrité de vos composants Vue.js de manière efficace.
Contexte réel : pourquoi un dev a besoin de ca au quotidien
Un développeur ne peut pas passer chaque jour à la main, inspecter chaque fonctionnalité pour s'assurer qu'elle fonctionne correctement. C'est là que les tests jouent un rôle crucial. Ils permettent d'automatiser le processus de vérification et de détecter rapidement tout problème qui pourrait affecter l'application.
Un cas d'utilisation concret en 2-3 phrases
Imaginez une application de gestion de tâches simple avec des fonctionnalités pour ajouter, modifier et supprimer des tâches. En utilisant Jest, vous pouvez créer des tests unitaires pour chaque fonctionnalité pour s'assurer qu'elles répondent correctement aux différents scénarios possibles.
Prerequis
Pour suivre ce tutoriel, il est nécessaire de disposer des connaissances suivantes :
- Connaissance de base de JavaScript et Vue.js
- Familiarité avec la syntaxe ES6+
- Compréhension des concepts de composants Vue (props, data, methods)
Vous aurez besoin d'installer les outils suivants :
- Node.js et npm : https://nodejs.org/
- Vue CLI : https://cli.vuejs.org/guide/installation.html
- Jest : https://jestjs.io/docs/getting-started
Concepts fondamentaux
1. Composants de base de Jest
Jest est composé de plusieurs éléments clés :
- Test Runner : Exécute les tests et génère un rapport
- Mocking : Permet de créer des objets simulés pour isoler le code à tester
- Snapshot Testing : Compare la sortie visuelle d'une fonction avec une capture d'écran précédente
2. Structure d'un Test Jest
Un test Jest est une fonction qui vérifie si un certain comportement est correct. Voici comment structurer un test :
// Exemple de test unitaire pour un composant Vue
import { shallowMount } from '@vue/test-utils';
import MyComponent from '@/components/MyComponent.vue';
describe('MyComponent', () => {
it('renders correctly', () => {
const wrapper = shallowMount(MyComponent);
expect(wrapper.exists()).toBe(true);
});
});
3. Mocks et Spies
Les mocks sont des objets qui remplacent les fonctions originales pour contrôler leur comportement lors des tests.
// Exemple de mock avec Jest
import MyService from '@/services/MyService';
import MyComponent from '@/components/MyComponent.vue';
jest.mock('@/services/MyService');
describe('MyComponent', () => {
it('calls a service method', async () => {
const wrapper = shallowMount(MyComponent);
await wrapper.vm.fetchData();
expect(MyService.getData).toHaveBeenCalled();
});
});
4. Snapshot Testing
Les tests de capture d'écran permettent de vérifier que la sortie visuelle d'une fonction n'a pas changé inattendument.
// Exemple de test de capture d'écran avec Jest
import MyComponent from '@/components/MyComponent.vue';
describe('MyComponent', () => {
it('renders correctly', () => {
const wrapper = shallowMount(MyComponent);
expect(wrapper.html()).toMatchSnapshot();
});
});
Mise en pratique : projet fil rouge
Dans cette section, nous allons créer un gestionnaire de tâches simple avec Jest pour tester ses composants.
Étape 1 : Initialiser le projet Vue.js
vue create task-manager
cd task-manager
npm install --save-dev jest @vue/test-utils babel-jest @babel/preset-env
Étape 2 : Créer un composant Task.vue
<!-- src/components/Task.vue -->
<template>
<div class="task">
<h3>task.title</h3>
<p>task.description</p>
<button @click="deleteTask">Delete</button>
</div>
</template>
<script>
export default {
props: {
task: {
type: Object,
required: true
}
},
methods: {
deleteTask() {
this.$emit('delete', this.task.id);
}
}
};
</script>
<style scoped>
.task {
border: 1px solid #ccc;
padding: 10px;
margin-bottom: 10px;
}
</style>
Étape 3 : Créer un test pour Task.vue
// src/components/__tests__/Task.spec.js
import { shallowMount } from '@vue/test-utils';
import Task from '@/components/Task.vue';
describe('Task', () => {
it('renders correctly with data', () => {
const wrapper = shallowMount(Task, {
propsData: {
task: { id: 1, title: 'Buy groceries', description: 'Milk and bread' }
}
});
expect(wrapper.text()).toContain('Buy groceries');
expect(wrapper.text()).toContain('Milk and bread');
});
it('emits delete event when button is clicked', () => {
const wrapper = shallowMount(Task, {
propsData: { task: { id: 1 } },
listeners: { delete: jest.fn() }
});
wrapper.find('button').trigger('click');
expect(wrapper.emitted().delete).toHaveBeenCalledWith(1);
});
});
Étape 4 : Créer un composant TaskManager.vue
<!-- src/components/TaskManager.vue -->
<template>
<div class="task-manager">
<h2>Task Manager</h2>
<ul>
<li v-for="task in tasks" :key="task.id">
<Task :task="task" @delete="removeTask" />
</li>
</ul>
<button @click="addTask">Add Task</button>
</div>
</template>
<script>
import Task from './Task.vue';
export default {
components: { Task },
data() {
return {
tasks: []
};
},
methods: {
addTask() {
this.tasks.push({ id: Date.now(), title: 'New Task', description: '' });
},
removeTask(id) {
this.tasks = this.tasks.filter(task => task.id !== id);
}
}
};
</script>
<style scoped>
.task-manager {
padding: 20px;
}
</style>
Étape 5 : Créer un test pour TaskManager.vue
// src/components/__tests__/TaskManager.spec.js
import { shallowMount } from '@vue/test-utils';
import TaskManager from '@/components/TaskManager.vue';
describe('TaskManager', () => {
it('renders correctly with tasks', () => {
const wrapper = shallowMount(TaskManager, {
data() {
return {
tasks: [
{ id: 1, title: 'Buy groceries', description: 'Milk and bread' },
{ id: 2, title: 'Clean the house', description: '' }
]
};
}
});
expect(wrapper.text()).toContain('Task Manager');
expect(wrapper.text()).toContain('Buy groceries');
expect(wrapper.text()).toContain('Clean the house');
});
it('emits delete event when task is deleted', () => {
const wrapper = shallowMount(TaskManager, {
data() {
return {
tasks: [{ id: 1, title: 'Task 1', description: '' }]
};
},
listeners: { delete: jest.fn() }
});
wrapper.find('button').trigger('click');
expect(wrapper.emitted().delete).toHaveBeenCalledWith(1);
});
it('adds a new task when button is clicked', () => {
const wrapper = shallowMount(TaskManager);
expect(wrapper.vm.tasks.length).toBe(0);
wrapper.find('button:last-child').trigger('click');
expect(wrapper.vm.tasks.length).toBe(1);
});
});
Erreurs fréquentes et debugging
Erreur 1 : TypeError: Cannot read property 'call' of undefined
// ❌ Mauvais
const mockFunction = jest.fn();
mockFunction();
// ✅ Correct
const mockFunction = jest.fn(() => {});
mockFunction();
Erreur 2 : ReferenceError: describe is not defined
// ❌ Mauvais
it('renders correctly', () => {
// test code here
});
// ✅ Correct
describe('MyComponent', () => {
it('renders correctly', () => {
// test code here
});
});
Erreur 3 : SyntaxError: Unexpected token import
// ❌ Mauvais
import { shallowMount } from '@vue/test-utils';
// ✅ Correct
const { shallowMount } = require('@vue/test-utils');
Pour aller plus loin
- Tests d'intégration : Apprenez à tester l'interaction entre différents composants.
- Mocking complexe : Découvrez comment gérer les mocks pour des fonctions asynchrones et des APIs externes.
- Coverage avec Istanbul : Intégrez Istanbul pour générer un rapport de couverture de code.
Défi pratique
Créez un composant LoginForm.vue qui permet à un utilisateur de se connecter. Ajoutez des tests unitaires pour vérifier les fonctionnalités suivantes :
- Le formulaire s'affiche avec les champs email et mot de passe.
- L'entrée du formulaire déclenche l'événement
submit. - La validation du formulaire vérifie que les champs ne sont pas vides.