如何正确测试Vuex?请求协助测试Vuex Store及关联按钮方法
测试Vuex Store关联的按钮交互
嘿,第一次搞Vuex测试确实会有点懵,我来帮你拆解成简单的步骤,咱们一步步验证这两个按钮的行为是否符合预期!
准备工作
首先确保你已经配置好测试环境:一般用@vue/test-utils(Vue测试工具库)搭配Jest(测试运行器)来写测试用例。我们需要模拟Vuex的store、$router还有socketUrl这些依赖,这样不用真正调用后端接口就能测试逻辑。
测试「登录」按钮(调用handleLogin)
这个方法的核心是调用this.$store.dispatch('login'),然后根据返回结果跳转路由。我们要验证:
- 点击按钮时,
dispatch('login')被正确调用,参数是表单数据 - 当
logindispatch返回包含id_user的响应时,会触发路由跳转
测试代码示例
import { mount } from '@vue/test-utils' import YourLoginComponent from '@/components/YourLoginComponent.vue' describe('Login Component - Login Button', () => { let wrapper let mockStore let mockRouter beforeEach(() => { // 模拟Vuex Store:mock dispatch方法,让它返回我们控制的Promise mockStore = { dispatch: jest.fn() } // 模拟Vue Router:mock push方法 mockRouter = { push: jest.fn() } // 挂载组件,注入模拟的store和router wrapper = mount(YourLoginComponent, { global: { mocks: { $store: mockStore, $router: mockRouter }, // 让登录按钮显示出来 data() { return { loginBtn: true, loginForm: { username: 'testuser', password: 'testpass' } } } } }) }) it('点击登录按钮时,调用store.dispatch("login")并传入表单数据', async () => { // 模拟事件对象(因为方法里调用了e.preventDefault()) const mockEvent = { preventDefault: jest.fn() } // 找到登录按钮并触发点击事件 const loginBtn = wrapper.find('.main-form__login-submit') await loginBtn.trigger('click', mockEvent) // 验证preventDefault被调用 expect(mockEvent.preventDefault).toHaveBeenCalled() // 验证dispatch被调用,参数正确 expect(mockStore.dispatch).toHaveBeenCalledWith('login', { username: 'testuser', password: 'testpass' }) }) it('当login返回含id_user的响应时,跳转到首页', async () => { // 让dispatch返回一个包含id_user的Promise mockStore.dispatch.mockResolvedValue({ id_user: 123 }) const mockEvent = { preventDefault: jest.fn() } const loginBtn = wrapper.find('.main-form__login-submit') await loginBtn.trigger('click', mockEvent) // 验证路由跳转被调用 expect(mockRouter.push).toHaveBeenCalledWith({ path: '/' }) }) it('当login返回不含id_user的响应时,不跳转路由', async () => { // 让dispatch返回不含id_user的Promise mockStore.dispatch.mockResolvedValue({}) const mockEvent = { preventDefault: jest.fn() } const loginBtn = wrapper.find('.main-form__login-submit') await loginBtn.trigger('click', mockEvent) // 验证路由跳转没被调用 expect(mockRouter.push).not.toHaveBeenCalled() }) })
测试「退出其他窗口」按钮(调用websocketAuth)
这个方法的逻辑更复杂:调用dispatch("logoutEverywhere"),然后触发socket事件,根据回调结果调用duplicateLoginClear。我们要验证:
- 点击按钮时,
dispatch("logoutEverywhere")被正确调用 - socket的
logoutEverywhere事件被触发,参数正确 - 当socket回调返回
true时,调用duplicateLoginClear
测试代码示例
import { mount } from '@vue/test-utils' import YourLoginComponent from '@/components/YourLoginComponent.vue' // 模拟socketUrl对象,mock emit方法 const mockSocketUrl = { emit: jest.fn((event, data, callback) => { // 默认让回调返回true,方便测试 callback(true) }) } describe('Login Component - Logout Everywhere Button', () => { let wrapper let mockStore beforeEach(() => { mockStore = { dispatch: jest.fn() } wrapper = mount(YourLoginComponent, { global: { mocks: { $store: mockStore, // 注入模拟的socketUrl socketUrl: mockSocketUrl }, // 让退出按钮显示出来 data() { return { showLogoutEverywhereBtn: true, loginForm: { username: 'testuser', password: 'testpass' } } } } }) // 每次测试前重置mock的调用记录,避免干扰 jest.clearAllMocks() }) it('点击按钮时,调用store.dispatch("logoutEverywhere")并传入表单数据', async () => { const mockEvent = { preventDefault: jest.fn() } const logoutBtn = wrapper.find('.main-form__login-session') await logoutBtn.trigger('click', mockEvent) expect(mockEvent.preventDefault).toHaveBeenCalled() expect(mockStore.dispatch).toHaveBeenCalledWith( 'logoutEverywhere', { user_login: 'testuser', user_password: 'testpass' } ) }) it('当logoutEverywhere返回结果后,触发socket的logoutEverywhere事件', async () => { // 让dispatch返回一个用户ID mockStore.dispatch.mockResolvedValue(123) const mockEvent = { preventDefault: jest.fn() } const logoutBtn = wrapper.find('.main-form__login-session') await logoutBtn.trigger('click', mockEvent) // 验证socket.emit被调用,参数正确 expect(mockSocketUrl.emit).toHaveBeenCalledWith( 'logoutEverywhere', { userId: 123, page: 'login' }, expect.any(Function) // 第三个参数是回调函数,只要验证它存在就行 ) }) it('当socket回调返回true时,调用duplicateLoginClear', async () => { mockStore.dispatch.mockResolvedValue(123) const mockEvent = { preventDefault: jest.fn() } const logoutBtn = wrapper.find('.main-form__login-session') await logoutBtn.trigger('click', mockEvent) // 因为我们在mock里让回调返回true,所以验证duplicateLoginClear被调用 expect(mockStore.dispatch).toHaveBeenCalledWith('duplicateLoginClear') }) it('当socket回调返回false时,不调用duplicateLoginClear', async () => { // 重新mock socket.emit,让回调返回false mockSocketUrl.emit.mockImplementation((event, data, callback) => { callback(false) }) mockStore.dispatch.mockResolvedValue(123) const mockEvent = { preventDefault: jest.fn() } const logoutBtn = wrapper.find('.main-form__login-session') await logoutBtn.trigger('click', mockEvent) // 验证duplicateLoginClear没被调用 expect(mockStore.dispatch).not.toHaveBeenCalledWith('duplicateLoginClear') }) })
一些关键提示
- Mock依赖是核心:我们不需要真正运行Vuex的action或者socket连接,只要验证组件是否正确调用了这些依赖即可。
- async/await不能忘:因为触发事件、Promise的执行都是异步的,必须用
await确保测试等待这些操作完成。 - 重置Mock:在每个测试用例前用
jest.clearAllMocks()清空调用记录,避免测试之间互相干扰。
内容的提问来源于stack exchange,提问作者Евлампий Романов
相关产品推荐
相关产品推荐

