D'après le titre, est-ce possible ?
J'essaie de tester un composant simple d'une application Vue qui contient un en-tête et un bouton qui redirige l'utilisateur vers la page suivante. Je veux tester si lorsque vous cliquez sur le bouton, un événement/message est envoyé au routeur et vérifier si l'itinéraire de destination est correct.
La candidature est structurée comme suit :
App.Vue - Composant d'entrée
<script setup lang="ts"> import { RouterView } from "vue-router"; import Wrapper from "@/components/Wrapper.vue"; import Container from "@/components/Container.vue"; import HomeView from "@/views/HomeView.vue"; </script> <template> <Wrapper> <Container> <RouterView/> </Container> </Wrapper> </template> <style> body{ @apply bg-slate-50 text-slate-800 dark:bg-slate-800 dark:text-slate-50; } </style>
router/index.ts - Fichier de configuration de Vue Router
import {createRouter, createWebHistory} from "vue-router"; import HomeView from "@/views/HomeView.vue"; export enum RouteNames { HOME = "home", GAME = "game", } const routes = [ { path: "/", name: RouteNames.HOME, component: HomeView, alias: "/home" }, { path: "/game", name: RouteNames.GAME, component: () => import("@/views/GameView.vue"), }, ]; const router = createRouter({ history: createWebHistory(import.meta.env.BASE_URL), routes }); export default router;
HomeView.Vue - La vue d'entrée de RouterView dans le composant App
<template> <Header title="My App"/> <ButtonRouterLink :to="RouteNames.GAME" text="Start" class="btn-game"/> </template> <script setup> import Header from "@/components/Header.vue"; import ButtonRouterLink from "@/components/ButtonRouterLink.vue"; import {RouteNames} from "@/router/"; </script>
ButtonRouterLink.vue
<template> <RouterLink v-bind:to="{name: to}" class="btn"> {{ text }} </RouterLink> </template> <script setup> import { RouterLink } from "vue-router"; const props = defineProps({ to: String, text: String }) </script>
Étant donné qu'il utilise CompositionAPI et TypeScript, existe-t-il un moyen de simuler le routeur dans les tests Vitest ?
Voici un exemple de structure de fichier de test (HomeView.spec.ts) :
import {shallowMount} from "@vue/test-utils"; import { describe, test, vi, expect } from "vitest"; import { RouteNames } from "@/router"; import HomeView from "../../views/HomeView.vue"; describe("Home", () => { test ('navigates to game route', async () => { // Check if the button navigates to the game route const wrapper = shallowMount(HomeView); await wrapper.find('.btn-game').trigger('click'); expect(MOCK_ROUTER.push).toHaveBeenCalledWith({ name: RouteNames.GAME }); }); });
J'ai essayé plusieurs méthodes : vi.mock('vue-router'), vi.spyOn(useRoute,'push'), bibliothèque vue-router-mock, mais je n'arrive pas à exécuter le test, il semble que le routeur n'est tout simplement pas disponible.
Suite à la suggestion de @tao, j'ai réalisé que tester les événements de clic dans HomeView n'était pas un bon test (c'était la bibliothèque qui a été testée, pas mon application), j'ai donc ajouté un test ButtonRouterLink pour voir si Vue RouterLink est rendu correctement , en utilisant les paramètres to :
import {mount, shallowMount} from "@vue/test-utils"; import { describe, test, vi, expect } from "vitest"; import { RouteNames } from "@/router"; import ButtonRouterLink from "../ButtonRouterLink.vue"; vi.mock('vue-router'); describe("ButtonRouterLink", () => { test (`correctly transforms 'to' param into a router-link prop`, async () => { const wrapper = mount(ButtonRouterLink, { props: { to: RouteNames.GAME } }); expect(wrapper.html()).toMatchSnapshot(); }); });
Il affiche une chaîne HTML vide""
exports[`ButtonRouterLink > correctly transforms 'to' param into a router-link prop 1`] = `""`;
Accompagné d'un avertissement Vue pas si utile (type attendu non spécifié) :
[Vue warn]: Invalid prop: type check failed for prop "to". Expected , got Object at <RouterLink to= { name: 'game' } class="btn btn-blue" > at <ButtonRouterLink to="game" ref="VTU_COMPONENT" > at <VTUROOT> [Vue warn]: Invalid prop: type check failed for prop "ariaCurrentValue". Expected , got String with value "page". at <RouterLink to= { name: 'game' } class="btn btn-blue" > at <ButtonRouterLink to="game" ref="VTU_COMPONENT" > at <VTUROOT> [Vue warn]: Component is missing template or render function. at <RouterLink to= { name: 'game' } class="btn btn-blue" > at <ButtonRouterLink to="game" ref="VTU_COMPONENT" > at <VTUROOT>
C'est possible.
Il y a une différence importante entre votre exemple et l'exemple officiel : dans le composant testé, vous l'avez placé dans un composant intermédiaire au lieu d'utiliser un bouton
RouterLink
(或使用router.push
).Comment cela va-t-il changer les tests unitaires ?
Le premier problème est que vous utilisez
shallowMount
shallowMount
,它会用空容器替换子组件。在你的情况下,这意味着ButtonRouterLink
不再包含RouterLink
, qui remplace le composant enfant par un conteneur vide. Dans votre cas, cela signifie queButtonRouterLink
ne contient plusRouterLink
et ne fait rien avec les événements de clic, il reçoit simplement les événements de clic.Les options pour résoudre ce problème sont :
mount
而不是shallowMount
et transformez-le en test d'intégration au lieu d'un test unitaireButtonRouterLink
的测试中。一旦测试了ButtonRouterLink
的任何:to
是否正确转换为{ name: to }
并传递给RouterLink
,你只需要测试这些:to
是否正确传递给使用它们的任何组件中的<ButtonRouterLink />
.Le deuxième problème créé par le déplacement de cette fonctionnalité dans son propre composant est légèrement plus subtil. En général, je m'attendrais à ce que le test suivant pour ButtonRouterLink fonctionne :
À ma grande surprise, ce test échoue avec le même
:to
属性值不是{ name: RouterNames.GAME }
,而是'game'
,这是我传递给ButtonRouterLink
的值。就像我们的组件从未将value
转换为{ name: value }
il prétend être reçu dans le RouterLinkStub.Assez étrange.
Il s’avère que le problème est que la valeur de
<RouterLink />
恰好是我们组件的根元素。这意味着组件(<BottomRouterLink>
)在DOM中被<RouterLinkStub>
替换,但:to
est lue à partir de la première et non de la seconde. Bref, si le modèle ressemble à ceci, le test réussira :Pour résoudre ce problème, nous devons importer la valeur réelle
RouterLink
组件(来自vue-router
),并找到它,而不是找到RouterLinkStub
。为什么?@vue/test-utils
足够聪明,可以找到存根组件而不是实际组件,但是这样,.findComponent()
只会匹配替换后的元素,而不是在它仍然是ButtonRouterLink
(未处理)时。此时,:to
的值已经被解析为RouterLink
reçue.Le test complet est le suivant :
Un peu délicat.