如何在客户端用Vitest与vitest-mock-extended模拟tRPC程序
前端单元测试中模拟tRPC客户端的问题与解决方案
问题描述
我尝试在前端单元测试中模拟tRPC客户端,无需启动tRPC后端服务即可执行测试。使用vitest-mock-extended的mock和mockDeep函数后,tRPC客户端仍会发起真实网络请求,导致连接被拒绝错误。vitest-mock-extended在tRPC后端应用中可正常工作。
普通类模拟正常
// 当userService不依赖tRPC时,模拟逻辑简单且正常工作 const userService = mock<UserService>(); // TypeScript能正确提示所有测试方法,mock生效 userService.createUser.mockResolvedValueOnce({"name": "Fake test user"});
tRPC程序模拟失败
// 使用tRPC时,TypeScript无法识别mockResolvedValueOnce方法 const userService = mock<typeof context.services.trpcClient.userService>(); // 仅能访问到`query`或`mutation`,无法调用mockResolvedValueOnce userService.createUser.mutation.mockResolvedValueOnce({"name": "Fake test user"}); userService.getUser.query.mockResolvedValueOnce({"name": "Fake test user"});
mockDeep尝试示例
const userService = mockDeep<typeof context.services.trpcClient.userService>(); userService.createUser.mutation.mockResolvedValueOnce({...}); userService.createUser.query.mockResolvedValueOnce({...});
测试环境为Vitest + Node/jsdom,求可行的tRPC客户端模拟方案。
解决方案
1. 正确深度mock嵌套结构
tRPC客户端的服务方法(如createUser)是嵌套对象,内部的mutation/query本身是可调用函数,必须通过mockDeep深度mock整个结构,才能让TypeScript识别到嵌套的mock方法:
import { mockDeep } from 'vitest-mock-extended'; // 深度mock整个tRPC user服务 const userService = mockDeep<typeof context.services.trpcClient.userService>(); // 直接为mutation/query方法设置mock返回值 userService.createUser.mutation.mockResolvedValueOnce({ name: "Fake test user" }); userService.getUser.query.mockResolvedValueOnce({ name: "Fake test user" });
2. 替换真实tRPC客户端实例
确保在测试中用mock后的客户端完全替换生产环境的实例,以避免真实网络请求。比如在React组件测试中,通过tRPC Provider注入mock客户端:
import { render, screen } from '@testing-library/react'; import { trpc } from '../path/to/trpc/client'; import { mockDeep } from 'vitest-mock-extended'; test('组件渲染mock的tRPC数据', async () => { // 深度mock user服务 const mockUserService = mockDeep<typeof trpc.userService>(); mockUserService.getUser.query.mockResolvedValueOnce({ name: "Mocked Test User" }); // 构建mock的tRPC客户端 const mockTrpcClient = { ...trpc, userService: mockUserService, }; // 通过Provider注入mock客户端 render( <trpc.Provider client={mockTrpcClient as any}> <UserProfile /> </trpc.Provider> ); // 断言组件渲染了mock数据 expect(await screen.findByText("Mocked Test User")).toBeInTheDocument(); });
3. 直接mock函数实现
如果mockDeep的自动mock不符合预期,可手动将mutation/query设为vitest的mock函数:
import { mockDeep } from 'vitest-mock-extended'; import vi from 'vitest'; const userService = mockDeep<typeof context.services.trpcClient.userService>(); // 手动替换为vitest mock函数并设置返回值 userService.createUser.mutation = vi.fn().mockResolvedValueOnce({ name: "Fake test user" });
内容的提问来源于stack exchange,提问作者Ben Butterworth
相关产品推荐
相关产品推荐

