我正在使用组合 API 学习 Vue 3,而且我对 Vitest 也很陌生(不过我知道它使用 Vue Test Utils)。
所以,简而言之,我遇到了一个问题,我模拟了 Vue Router,触发了对 router-link 元素的点击,并期望路由器被调用,但它失败了。
首先,为了以防万一,这些是我在可能与此事相关的依赖项上使用的版本:
"dependencies": {
"vue": "^3.3.2",
"vue-router": "^4.2.0"
},
"devDependencies": {
"@vitejs/plugin-vue": "^4.2.3",
"@vitejs/plugin-vue-jsx": "^3.0.1",
"@vue/test-utils": "^2.3.2",
"jsdom": "^22.0.0",
"start-server-and-test": "^2.0.0",
"vite": "^4.3.5",
"vitest": "^0.31.0"
}
我有一个简单的视图来测试这个:
<template>
<router-link data-test="test-link" to="login">test link</router-link>
</template>
我已经尝试了几种方法来进行测试,但这里是我认为更有意义的 2 种:
1 - 使用mockImplementationOnce,来自VTU 文档
import { describe, expect, test, vi } from 'vitest'
import { mount } from '@vue/test-utils'
import { useRouter } from 'vue-router'
import TestView from '../../views/TestView.vue'
vi.mock('vue-router', () => ({
useRoute: vi.fn(),
useRouter: vi.fn(() => ({
push: () => {}
}))
}))
describe('Test View', () => {
test('Login button redirects to login view', async () => {
const push = vi.fn()
useRouter.mockImplementationOnce(() => ({
push
}))
const wrapper = mount(TestView, {
global: {
stubs: ['router-link'] // tried removing this stub aswell
}
})
const btn = wrapper.get('[data-test="test-link"]')
await btn.trigger('click')
// Few things to note here:
// I also tried adding await nextTick() and even a promise with 1s timeout to make sure
// the button is clicked before the assertion happens, but doesn't help.
// Also it's not like the button isn't found, the following assert succeeds
expect(btn.attributes('to')).toEqual('login')
// Output: AssertionError: expected "spy" to be called at least once
expect(push).toHaveBeenCalled()
})
})
2-使用mockReturnValue,在这篇文章
中看到import { beforeEach, describe, expect, test, vi } from 'vitest'
import { mount } from '@vue/test-utils'
import { useRouter } from 'vue-router'
import TestView from '../../views/TestView.vue'
vi.mock('vue-router')
describe('Test View', () => {
useRouter.mockReturnValue({
push: vi.fn()
})
beforeEach(() => {
useRouter().push.mockReset()
})
test('Login button redirects to login view', async () => {
const wrapper = mount(TestView, {
global: {
stubs: ['router-link'] // tried removing this stub aswell
}
})
const btn = wrapper.get('[data-test="test-link"]')
await btn.trigger('click')
// Few things to note here:
// I also tried adding await nextTick() and even a promise with 1s timeout to make sure
// the button is clicked before the assertion happens, but doesn't help.
// Also it's not like the button isn't found, the following assert succeeds
expect(btn.attributes('to')).toEqual('login')
// Output: AssertionError: expected "spy" to be called at least once
expect(useRouter().push).toHaveBeenCalled()
})
})
因此,在这两种情况下我都会遇到相同的错误:
AssertionError: expected "spy" to be called at least once
我发现的所有其他潜在解决方案都不适合我,其中许多很可能已经过时了。
有人看到我缺少什么或者是否有其他方法可以做到这一点?
提前感谢您,如果我没有完全遵循指南,请抱歉,这是我第一次在这里问问题。
我面临着同样的问题,终于找到了一种方法来解决它。让我知道它是否也适合您:
import { afterEach, describe, expect, it, vi } from 'vitest'
import { render } from '@/test-utils'
import userEvent from '@testing-library/user-event'
afterEach(() => {
vi.clearAllMocks()
})
const mockRoutePush = vi.fn()
vi.mock('vue-router', async () => {
return {
RouterView: {},
useRouter: () => {
return {
push: mockRoutePush
}
}
}
})
describe('when ...', () => {
it('navigates to ...', async () => {
// set up everything
const { screen } = render(MyComponent)
// click on button
const user = userEvent.setup()
await user.click(screen.getByRole('button', { name: 'navigate' }))
// expect that navigation happened
expect(mockRoutePush).toHaveBeenCalled()
expect(mockRoutePush).toHaveBeenCalledWith(
expect.objectContaining({ name: 'route-name' })
)
})
})