You’re browsing the documentation for Vue Test Utils for Vue v2.x and earlier.
To read docs for Vue Test Utils for Vue 3, click here.
Les Conseils pratiques
Savoir ce qu'il faut tester
Pour les composants UI, nous ne recommandons pas de viser une couverture complète par ligne, car cela conduit à trop se concentrer sur les détails de mise en œuvre interne des composants et pourrait entraîner des tests fragiles.
Nous recommandons plutôt de rédiger des tests qui affirment l'interface publique de notre composant et de traiter ses éléments internes comme une boîte noire. Un seul cas de test permettrait d'affirmer qu'une entrée (interaction avec l'utilisateur ou changement de props) fournie au composant se traduit par la sortie attendue (résultat de rendu ou événements personnalisés émis).
Par exemple, imaginez un composant Counter
qui incrémente un compteur d'affichage de 1 chaque fois qu'un bouton est cliqué. Le test ne devrait pas se soucier de la façon dont le Counter
incrémente la valeur - il ne s'intéresse qu'à l'entrée et à la sortie.
L'avantage de cette approche est que tant que l'interface publique de votre composant reste la même, vos tests seront réussis, quelle que soit l'évolution de l'implémentation interne du composant dans le temps.
Ce sujet est abordé plus en détail dans une excellente présentation de Matt O'Connell.
Le montage Shallow
Parfois, le montage d'un composant entier avec toutes ses dépendances peut devenir lent ou lourd. Par exemple, les composants qui contiennent de nombreux composants enfants.
Vue Test Utils vous permet de monter un composant sans rendre ses composants enfants (en les "stubbing") avec la méthode shallowMount
.
import { shallowMount } from '@vue/test-utils'
import Component from '../Component.vue'
const wrapper = shallowMount(Component)
Comme pour mount, il crée un Wrapper qui contient le composant Vue monté et rendu, mais avec des composants enfants.
Notez que l'utilisation de shallowMount
rendra le composant testé différent du composant que vous exécutez dans votre application - certaines de ses parties ne seront pas rendues ! C'est pourquoi ce n'est pas la façon recommandée de tester les composants, sauf si vous rencontrez des problèmes de performance ou si vous avez besoin de simplifier les dispositions de test.
Les Hooks de cycle de vie
Lorsque vous utilisez les méthodes mount
ou shallowMount
, vous pouvez vous attendre à ce que votre composant réponde à tous les événements du cycle de vie. Cependant, il est important de noter que les méthodes beforeDestroy
et destroyed
ne seront pas déclanchées à moins que le composant ne soit détruit manuellement en utilisant Wrapper.destroy()
.
De plus, le composant ne sera pas automatiquement détruit à la fin de chaque spécification, et c'est à l'utilisateur d'arrêter ou de nettoyer manuellement les tâches qui continueront à s'exécuter (setInterval
ou setTimeout
, par exemple) avant la fin de chaque spec.
Écrire des tests asynchrones (nouveau)
Par défaut, Vue regroupe les mises à jour pour les exécuter de manière asynchrone (à la prochaine "coche"). Ceci afin d'éviter les re-rendus DOM inutiles, et les calculs des watchers (voir les docs pour plus de détails).
Cela signifie que vous devez attendre que les mises à jour s'exécutent après avoir modifié une propriété réactive. Vous pouvez le faire en attendant les méthodes de mutations comme le trigger
:
it('updates text', async () => {
const wrapper = mount(Component)
await wrapper.trigger('click')
expect(wrapper.text()).toContain('updated')
await wrapper.trigger('click')
wrapper.text().toContain('some different text')
})
// Ou si vous êtes sans async/await
it('render text', done => {
const wrapper = mount(TestComponent)
wrapper.trigger('click').then(() => {
wrapper.text().toContain('updated')
wrapper.trigger('click').then(() => {
wrapper.text().toContain('some different text')
done()
})
})
})
Pour en savoir plus, consultez la page Tester le comportement asynchrone
Affirmer les événements émis
Chaque wrapper monté enregistre automatiquement tous les événements émis par l'instance Vue sous-jacente. Vous pouvez récupérer les événements enregistrés en utilisant la méthode wrapper.emitted()
:
wrapper.vm.$emit('foo')
wrapper.vm.$emit('foo', 123)
/*
`wrapper.emitted()` renvoie l'objet suivant :
{
foo: [[], [123]]
}
*/
Vous pouvez alors faire des affirmations sur la base de ces données :
// affirmer l'émission de l'événement
expect(wrapper.emitted().foo).toBeTruthy()
// affirmer le nombre d'événements
expect(wrapper.emitted().foo.length).toBe(2)
// affirmer le paramètre additionnel
expect(wrapper.emitted().foo[1]).toEqual([123])
Vous pouvez également obtenir un tableau des événements dans leur ordre d'émission en appelant wrapper.emittedByOrder()
.
Événement émis d'un composant Enfant
Vous pouvez émettre un événement personnalisé à partir d'un composant enfant en accédant à l'instance.
Composant en cours de test
<template>
<div>
<child-component @custom="onCustom" />
<p v-if="emitted">Emitted!</p>
</div>
</template>
<script>
import ChildComponent from './ChildComponent'
export default {
name: 'ParentComponent',
components: { ChildComponent },
data() {
return {
emitted: false
}
},
methods: {
onCustom() {
this.emitted = true
}
}
}
</script>
Test
import { mount } from '@vue/test-utils'
import ParentComponent from '@/components/ParentComponent'
import ChildComponent from '@/components/ChildComponent'
describe('ParentComponent', () => {
it("displays 'Emitted!' when custom event is emitted", () => {
const wrapper = mount(ParentComponent)
wrapper.find(ChildComponent).vm.$emit('custom')
expect(wrapper.html()).toContain('Emitted!')
})
})
Manipulation de l'état des composants
Vous pouvez directement manipuler l'état du composant en utilisant la méthode setData
ou setProps
dans le wrapper :
wrapper.setData({ count: 10 })
wrapper.setProps({ foo: 'bar' })
Simuler les Props
Vous pouvez passer les props au composant en utilisant l'option intégrée propsData
de Vue :
import { mount } from '@vue/test-utils'
mount(Component, {
propsData: {
aProp: 'some value'
}
})
Vous pouvez également mettre à jour les props d'un composant déjà monté avec la méthode wrapper.setProps({})
.
_Pour une liste complète des options, vueillez consulter la section des options de montage de la documentation.
Simulation de transition
Bien qu'appeler await Vue.nextTick()
fonctionne bien pour la plupart des cas d'utilisation, il y a certaines situations où des solutions de contournement supplémentaire sont nécessaires. Ces problèmes seront résolus avant que la bibliothèque vue-test-utils
ne sorte de la version bêta. Un exemple est celui des composants de test unitaire avec le wrapper <transition>
fourni par Vue.
<template>
<div>
<transition>
<p v-if="show">Foo</p>
</transition>
</div>
</template>
<script>
export default {
data() {
return {
show: true
}
}
}
</script>
Vous pourriez vouloir écrire un test qui vérifie que Foo est affiché, puis lorsque show
est réglé sur false
, Foo n'est plus rendu. Un tel test pourrait être écrit de la manière suivante :
test('should render Foo, then hide it', async () => {
const wrapper = mount(Foo)
expect(wrapper.text()).toMatch(/Foo/)
await wrapper.setData({
show: false
})
expect(wrapper.text()).not.toMatch(/Foo/)
})
En pratique, bien que nous appelions et attendions setData
pour assurer la mise à jour du DOM, ce test échoue. Il s'agit d'un problème permanent lié à la façon dont Vue implémente le composant <transition>
, que nous aimerions résoudre avant la version 1.0. Pour l'instant, il existe quelques solutions de contournement :
transitionStub
Utiliser un helper const transitionStub = () => ({
render: function (h) {
return this.$options._renderChildren
}
})
test('should render Foo, then hide it', async () => {
const wrapper = mount(Foo, {
stubs: {
transition: transitionStub()
}
})
expect(wrapper.text()).toMatch(/Foo/)
await wrapper.setData({
show: false
})
expect(wrapper.text()).not.toMatch(/Foo/)
})
Cela remplace le comportement par défaut du composant <transition>
et affiche les enfants dès que la condition booléenne pertinente change, par opposition à l'application de classes CSS, qui est la façon dont le composant <transition>
de Vue fonctionne.
setData
Éviter Une autre solution est tout simplement d'éviter d'utiliser setData
en écrivant deux tests, la configuration requise étant effectuée à l'aide des options mount
et shallowMount
:
test('should render Foo', async () => {
const wrapper = mount(Foo, {
data() {
return {
show: true
}
}
})
expect(wrapper.text()).toMatch(/Foo/)
})
test('should not render Foo', async () => {
const wrapper = mount(Foo, {
data() {
return {
show: false
}
}
})
expect(wrapper.text()).not.toMatch(/Foo/)
})
Appliquer les plugins et les mixins globaux
Certains des composants peuvent reposer sur des fonctionnalités injectées par un plugin ou par un mixin global, par exemple vuex
et vue-router
.
Si vous écrivez des tests pour des composants dans une application spécifique, vous pouvez configurer les mêmes plugins et mixins globaux une fois dans l'entrée de vos tests. Mais dans certains cas, par exemple pour tester une suite de composants génériques qui peuvent être partagés entre différentes applications, il est préférable de tester vos composants dans une configuration plus isolée, sans polluer le constructeur global "Vue". Nous pouvons utiliser la méthode createLocalVue
pour y parvenir :
import { createLocalVue, mount } from '@vue/test-utils'
// créer un constructeur de `Vue` étendu
const localVue = createLocalVue()
// installer les plugins comme d'habitude
localVue.use(MyPlugin)
// passer la `localVue` aux options de montage
mount(Component, {
localVue
})
À noter: certains plugins, comme Vue Router, ajoutent des propriétés en lecture seule au constructeur global de Vue. Cala rend impossible de réinstaller le plugin sur un constructeur localVue
, ou d'ajouter des mocks pour ces propriétés en lecture seule
Injection Simulée
Une autre stratégie pour les props injectés consiste simplement à les simuler. Vous pouvez le faire avec l'option mocks
:
import { mount } from '@vue/test-utils'
const $route = {
path: '/',
hash: '',
params: { id: '123' },
query: { q: 'hello' }
}
mount(Component, {
mocks: {
// ajouter l'objet `$route` simlué à l'instance Vue
// avant le montage du composant
$route
}
})
Les composants d'écrasement
Vous pouvez remplacer les composants qui sont enregistrés globalement ou localement en utilisant l'option stubs
:
import { mount } from '@vue/test-utils'
mount(Component, {
// Résoudra le problème des composants enregistrés au niveau mondial avec
// le stub vide
stubs: ['globally-registered-component']
})
Gestion du routage
Étant donné que le routage, par définition, a trait à la structure globale de l'application et implique de multiples composants, il est préférable de le tester par des tests d'intégration ou de bout en bout. Pour les composants individuels qui s'appuient sur les fonctionnalités de vue-router
, vous pouvez les simuler en utilisant les techniques mentionnées ci-dessus.
Détecter les styles
Votre test ne peut détecter que les styles en ligne lorsqu'il est exécuté dans jsdom
.