在Next.js中用Tsyringe+Playwright实现DI:解决未注册依赖错误
解决Next.js + tsyringe 测试环境DI注入失败问题
核心问题定位
测试时抛出Error: Attempted to resolve unregistered dependency token: "IUser",本质是Playwright测试环境中,tsyringe容器未注册IUser对应的Mock实现,或者生产环境的注册逻辑在测试时没有被正确覆盖。
具体解决方案
1. 拆分DI注册逻辑,区分生产/测试环境
把容器注册代码拆成独立文件,避免硬编码实现类,方便测试时替换:
- 创建
di-registry.ts(生产环境注册):
import { container } from 'tsyringe'; import { IUser } from './interfaces'; import { User } from './User'; export const registerProductionDependencies = () => { container.register<IUser>('IUser', { useClass: User }); };
- 创建
di-registry.mock.ts(测试环境注册):
import { container } from 'tsyringe'; import { IUser } from './interfaces'; import { UserMock } from './UserMock'; export const registerTestDependencies = () => { container.clearInstances(); // 清空生产环境的实例残留 container.register<IUser>('IUser', { useClass: UserMock }); };
2. 修改instrumentation.ts,根据环境加载注册逻辑
Next.js的instrumentation.ts会在应用启动时执行,这里要判断环境:
import { registerProductionDependencies } from './di-registry'; export async function register() { if (process.env.NODE_ENV === 'production' || process.env.NODE_ENV === 'development') { registerProductionDependencies(); } }
3. 在Playwright测试中手动注册Mock依赖
测试用例执行前,需要先调用测试环境的注册函数,确保容器里有Mock实现:
import { test, expect } from '@playwright/test'; import { registerTestDependencies } from '../src/di-registry.mock'; test.beforeAll(() => { // 测试启动前注册Mock依赖 registerTestDependencies(); }); test('测试DI注入Mock类', async ({ page }) => { await page.goto('/di'); // 断言页面上显示Mock类的内容,比如UserMock返回的固定值 await expect(page.getByText('Mock User Data')).toBeVisible(); });
4. 确保页面组件正确获取依赖
页面di.tsx中要通过tsyringe的resolve方法获取实例,注意不要提前初始化:
import { container } from 'tsyringe'; import { IUser } from './interfaces'; export default function DIPage() { const user = container.resolve<IUser>('IUser'); return <div>{user.getData()}</div>; }
备选方案:改用@nestjs/core的DI(如果tsyringe适配困难)
如果tsyringe在Next.js测试环境的适配问题多,可以用NestJS的DI容器,步骤更清晰:
- 安装依赖:
npm install @nestjs/core @nestjs/common - 创建生产模块:
import { Module } from '@nestjs/common'; import { IUser } from './interfaces'; import { User } from './User'; @Module({ providers: [{ provide: 'IUser', useClass: User }], exports: ['IUser'], }) export class AppModule {}
- 测试时替换提供者:
import { Test } from '@nestjs/testing'; import { AppModule } from './app.module'; import { UserMock } from './UserMock'; test.beforeAll(async () => { const moduleRef = await Test.createTestingModule({ imports: [AppModule], }) .overrideProvider('IUser') .useClass(UserMock) .compile(); // 可将模块实例全局挂载,或在页面组件中通过moduleRef.get('IUser')获取实例 });
关键注意事项
- 测试时一定要清空容器实例,避免生产环境的实例残留
- 确保
process.env.NODE_ENV在测试时被正确设置为test - Next.js App Router中,instrumentation.ts仅在服务端执行,客户端组件的DI需要通过
getServerSideProps或服务端组件传递实例给客户端
内容的提问来源于stack exchange,提问作者hirossyi73
相关产品推荐
相关产品推荐

