sho*_*oop 6 typescript vue-test-utils vuejs3 vue-composition-api vue-router4
由于安装过程中设置方法中未定义路由对象,我无法在单元测试期间安装组件。这些指南似乎针对的是 Vue2 和选项 API
\n参考文献:\n
\n
\n如何编写模拟 vue 组件中 $route 对象的测试\n
\n如何在 vue 组合 api 组件中使用 jest 进行单元测试?\n
\n https://vue-test-utils.vuejs.org/guides/#using-with-typescript \
n \n https://vue-test-utils.vuejs.org/guides/#using-with -vue-路由器
错误
\n\xe2\x97\x8f CoachItem.vue \xe2\x80\xba displays alert when item is clicked\n\n TypeError: Cannot read property \'path\' of undefined\n\n 64 | fullName: computed(() => props.firstName + " " + props.lastName),\n 65 | coachContactLink: computed(\n > 66 | () => route.path + "/" + props.id + "/contact"\n\nRun Code Online (Sandbox Code Playgroud)\n// @/tests/unit/example.spec.ts\n\nimport CoachItem from "@/components/coaches/CoachItem.vue"\nimport router from "@/router"\n\n\n describe("CoachItem.vue", () => {\n it("displays alert when item is clicked", async () => {\n\n //const route = { path: \'http://www.example-path.com\' }\n router.push(\'/\')\n await router.isReady()\n const wrapper = mount(CoachItem); //adding this line causes failure\n //await wrapper.trigger(\'click\');\n //const dialog = wrapper.find(\'dialog\');\n //(dialog.exists()).toBeTruthy()\n })\n })\nRun Code Online (Sandbox Code Playgroud)\n// @/components/UserAlert.vue\n\n<template>\n <div class="backdrop" @click="closeDialog"></div>\n <dialog open>\n <header>\n <h2>{{ title }}</h2>\n </header>\n <div>\n <slot name="content"></slot>\n </div>\n <menu>\n <button @click="closeDialog">Close</button>\n </menu>\n </dialog>\n</template>\n\n<script lang="ts>\nimport { defineComponent } from "vue";\n\nexport default defineComponent({\n props: [\'title\'],\n emits: [\'close\'],\n setup(_, context) {\n function closeDialog() {\n context.emit(\'close\');\n }\n\n return { closeDialog };\n },\n});\n</script>\nRun Code Online (Sandbox Code Playgroud)\n// @/components/coaches.CoachItem.vue\n\n<template>\n<user-alert v-if="alertIsVisible" title="Alert!" @close="hideAlert">\n <template v-slot:content><p>this is a slot</p></template>\n </user-alert>\n <li @click="showAlert">\n <h3>{{ fullName }}</h3>\n <h4>${{ rate }}/hour</h4>\n <div>\n <base-badge\n v-for="area in areas"\n :key="area"\n :type="area"\n :title="area"\n ></base-badge>\n </div>\n <div class="actions">\n <base-button mode="outline" link :to="coachContactLink"\n >Contact</base-button\n >\n <base-button link :to="coachDetailsLink">View Details</base-button>\n </div>\n </li>\n</template>\n\n<script lang="ts">\nimport { computed, defineComponent, PropType, ref } from "vue";\nimport { useRoute } from "vue-router";\nimport useAlert from "../../components/hooks/useAlert";\nexport default defineComponent({\n props: {\n id: {\n type: String,\n required: true,\n },\n firstName: {\n type: String,\n required: true,\n },\n lastName: {\n type: String,\n required: true,\n },\n rate: {\n type: Number,\n required: true,\n },\n areas: {\n type: Object as PropType<Array<string>>,\n required: true,\n },\n },\n setup(props) {\n const route = useRoute();\n const alertTitle = ref("delete user?");\n return {\n fullName: computed(() => props.firstName + " " + props.lastName),\n coachContactLink: computed(\n () => route.path + "/" + props.id + "/contact"\n ),\n coachDetailsLink: computed(() => route.path + "/" + props.id),\n ...useAlert()\n };\n },\n});\n</script>\nRun Code Online (Sandbox Code Playgroud)\n// @/main.ts\nimport { createApp } from "vue";\nimport App from "./App.vue";\nimport router from "./router";\nimport {store, key } from "./store";\nimport UserAlert from "@/components/UserAlert.vue";\n\ncreateApp(App)\n.component(\'UserAlert\', UserAlert)\n .use(store, key)\n .use(router)\n .mount("#app");\nRun Code Online (Sandbox Code Playgroud)\n// @/router/index.ts\n\nconst router = createRouter({\n history: createWebHistory(process.env.BASE_URL),\n routes\n});\n\nexport default router;\nRun Code Online (Sandbox Code Playgroud)\n
查看vitest Issue #1918中的示例和vue-test-utils 组合文档
以下模拟允许组件使用useRouter或useRoute来工作:
import { mount } from '@vue/test-utils'
import { expect, it, vi } from 'vitest'
import CompWithRoute from './CompWithRoute.vue'
vi.mock('vue-router', () => {
return {
useRouter: vi.fn(() => ({
push: vi.fn(),
})),
useRoute: vi.fn(()=> ({
fullPath: '',
hash: '',
matched: [],
meta: {},
name: undefined,
params: {},
path: '/guppies',
query: {
search: 'ireland'
},
redirectedFrom: undefined,
}))
}
})
it('should render the route loving component', () => {
const wrapper = mount(CompWithRoute)
})
Run Code Online (Sandbox Code Playgroud)
如果需要测试调用,mockImplementationOnce可以注入间谍(尽管打字稿不喜欢模拟的松散实现Router)
import { mount } from '@vue/test-utils'
import { afterEach, expect, it, vi } from 'vitest'
import * as routerExports from 'vue-router'
import CompWithRoute from './CompWithRoute.vue'
const useRouter = vi.spyOn(routerExports, 'useRouter')
afterEach(() => {
vi.clearAllMocks()
})
it('should push a new route on search', async () => {
const push = vi.fn()
useRouter.mockImplementationOnce(() => ({
push
}))
const wrapper = mount(CompWithRoute)
const search = wrapper.find('#search-input')
await search.setValue('ireland')
await search.trigger('keyup.enter')
expect(push).toHaveBeenCalledWith({ query: { search: 'ireland'} })
})
Run Code Online (Sandbox Code Playgroud)
还可以将全局间谍注入到提升的模拟Router实现中,并在您的期望中引用它。
const mock_push = vi.fn()
vi.mock('vue-router', () => ({
useRouter: vi.fn(() => ({
push: mock_push,
})),
}))
afterEach(() => {
vi.clearAllMocks()
})
it('should push a new route on search', async () => {
const wrapper = mount(CompWithRoute)
const search = wrapper.find('#search-input')
await search.setValue('ireland')
await search.trigger('keyup.enter')
expect(mock_push).toHaveBeenCalledWith({ query: { search: 'ireland'} })
})
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1231 次 |
| 最近记录: |