Vue+Jest环境下如何Mock window.gapi.load等谷歌授权方法?
解决Vue.js中Google Auth单元测试的Mock问题
我帮你梳理一下如何在Jest + vue-test-utils中Mock Google Auth的相关方法,确保你的npm run test:unit能顺利执行无报错。核心思路是提前Mock好window.gapi的整个结构,包括它的异步方法回调,同时覆盖组件内的自定义方法验证。
第一步:全局Mock window.gapi 对象
Google Auth的API都是挂载在window.gapi下的,所以我们需要在测试文件顶部先构建一个完整的Mock对象,关键是要处理gapi.load的异步回调(组件里依赖这个回调触发后续的auth2.init)。
// Login.spec.js 顶部 // 构建Mock的gapi对象 const mockGapi = { // 模拟gapi.load:直接调用传入的回调函数,模拟加载完成 load: jest.fn((apiName, callback) => callback()), auth2: { // 模拟auth2.init,这里组件没用到返回值,用jest.fn()即可 init: jest.fn() }, signin2: { // 模拟signin2.render方法 render: jest.fn() } }; // 将Mock对象挂载到window上 window.gapi = mockGapi;
第二步:编写测试用例覆盖所有场景
接下来我们针对你需要验证的5个方法,编写对应的测试用例,同时确保组件挂载、方法调用的流程正确。
前置准备:Mock Vue Router
因为你的onSuccess方法里用到了$router.push,所以需要在挂载组件时注入Mock的路由实例:
import { mount } from '@vue/test-utils'; import Login from '@/components/Login.vue'; // 如果是Vue2,导入VueRouter并使用Vue.use(VueRouter) import { createRouter, createWebHistory } from 'vue-router'; // 创建Mock路由 const router = createRouter({ history: createWebHistory(), routes: [{ path: '/homepage', name: 'Homepage' }] }); // 重置Mock的辅助函数 beforeEach(() => { jest.clearAllMocks(); });
测试用例1:验证组件挂载后的流程
验证mounted钩子触发renderButton,进而调用init、gapi.load、auth2.init和signin2.render:
it('triggers Google Auth button rendering on mount', () => { const wrapper = mount(Login, { global: { plugins: [router] // 注入路由 } }); // 断言组件的renderButton方法被调用 expect(wrapper.vm.renderButton).toHaveBeenCalled(); // 断言gapi.load被调用,参数是'auth2'和回调函数 expect(mockGapi.load).toHaveBeenCalledWith('auth2', expect.any(Function)); // 断言auth2.init传入了正确的配置 expect(mockGapi.auth2.init).toHaveBeenCalledWith({ client_id: 'xyz', ux_mode: 'redirect' }); // 断言signin2.render传入了正确的容器ID和配置 expect(mockGapi.signin2.render).toHaveBeenCalledWith('my-signin2', expect.objectContaining({ scope: 'profile email', width: 240, height: 50, longtitle: true, theme: 'dark', onsuccess: wrapper.vm.onSuccess, onfailure: wrapper.vm.onFailure })); });
测试用例2:验证onSuccess方法的路由跳转
it('navigates to homepage on successful login', async () => { const wrapper = mount(Login, { global: { plugins: [router] } }); // 调用onSuccess方法,随便传个GoogleUser对象(组件没用到它的属性) await wrapper.vm.onSuccess({}); // 断言路由push被调用,目标路径是'/homepage' expect(router.push).toHaveBeenCalledWith('/homepage'); });
测试用例3:验证onFailure方法的日志输出
it('logs error message on login failure', () => { // Mock console.log,避免测试时真的打印日志 const consoleLogMock = jest.spyOn(console, 'log').mockImplementation(); const wrapper = mount(Login); wrapper.vm.onFailure(); // 断言console.log输出了指定内容 expect(consoleLogMock).toHaveBeenCalledWith('try again'); // 恢复原有的console.log consoleLogMock.mockRestore(); });
测试用例4:验证renderButton的调用逻辑
it('calls init and signin2.render when renderButton is invoked', () => { const wrapper = mount(Login); // 清空之前的Mock调用记录 jest.clearAllMocks(); wrapper.vm.renderButton(); // 断言init方法被调用 expect(wrapper.vm.init).toHaveBeenCalled(); // 断言signin2.render被调用 expect(mockGapi.signin2.render).toHaveBeenCalled(); });
关键注意事项
- 异步回调处理:
gapi.load是异步方法,组件里依赖它的回调触发auth2.init,所以Mock时必须主动调用传入的回调函数,否则后续逻辑不会执行。 - Mock重置:每个测试用例前用
jest.clearAllMocks()重置Mock的调用记录,避免用例间的干扰。 - Vue版本适配:如果是Vue2,路由的注入方式是在挂载选项里传入
router属性,而不是global.plugins,比如:const wrapper = mount(Login, { router });
现在把这些代码整合到你的Login.spec.js里,执行npm run test:unit应该就能顺利通过所有测试啦!
内容的提问来源于stack exchange,提问作者Rahul Sharma
相关产品推荐
相关产品推荐

