You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在客户端用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 23:55:36