所以,我有这个使用 SWC 和 Vitest 的 Nest.js API 应用程序。这是用户模块的最小化文件。
// users.service.ts
...
@Injectable()
@UseInterceptors(ClassSerializerInterceptor)
export class UsersService {
constructor(
@InjectRepository(UserEntity)
private readonly userRepository: EntityRepository<UserEntity>,
private readonly em: EntityManager,
) {}
@EnsureRequestContext()
async create(createUserDto: CreateUserDto): Promise<UserEntity> {
const user = this.userRepository.create(createUserDto);
await this.em.flush();
return new UserEntity(user);
}
...
}
// users.controller.ts
...
@Controller("users")
export class UsersController {
constructor(private readonly usersService: UsersService) {}
@Post()
@ApiCreatedResponse({ type: UserEntity })
async create(@Body() createUserDto: CreateUserDto): Promise<UserEntity> {
return this.usersService.create(createUserDto);
}
...
}
// users.module.ts
...
@Module({
controllers: [UsersController],
exports: [UsersService],
imports: [MikroOrmModule.forFeature([UserEntity])],
providers: [UsersService],
})
export class UsersModule {}
// users.e2e-spec.ts
...
describe("Users", () => {
let app: NestFastifyApplication;
let orm: MikroORM;
let payload: UserEntity[];
let userRepository: EntityRepository<UserEntity>;
let usersService: Partial<UsersService> = {
findAll: vi.fn().mockResolvedValue(payload),
};
beforeAll(async () => {
const module: TestingModule = await Test.createTestingModule({
imports: [UsersModule],
providers: [
{
provide: EntityManager,
useFactory: vi.fn(() => ({
flush: vi.fn().mockImplementation(() => {
return Promise.resolve();
}),
})),
},
{
provide: getRepositoryToken(UserEntity),
useValue: userRepository,
},
],
})
.overrideProvider(UsersService)
.useValue(usersService)
.compile();
app = module.createNestApplication<NestFastifyApplication>(
new FastifyAdapter(),
);
await app.init();
await app.getHttpAdapter().getInstance().ready();
orm = app.get(MikroORM);
payload = new UserFactory(orm.em.fork()).make(2);
});
it("/GET users", async () => {
return app
.inject({
method: "GET",
url: "/users",
})
.then((result) => {
expect(result.statusCode).toEqual(200);
expect(result.payload).toEqual(payload);
});
});
afterAll(async () => {
await app.close();
});
});
当我运行
npm run test:e2e -- src/users/users.e2e-spec.ts
时出现以下错误:
> test:e2e
> vitest run --config ./vitest.config.e2e.ts src/users/users.e2e-spec.ts
The CJS build of Vite's Node API is deprecated. See https://vitejs.dev/guide/troubleshooting.html#vite-cjs-node-api-deprecated for more details.
RUN v1.3.1 /home/me/apps/api
❯ src/users/users.e2e-spec.ts (1)
❯ Users (1)
⠹ [ beforeAll ]
· /GET users
⠹ [ afterAll ]
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ Failed Suites 1 ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
FAIL src/users/users.e2e-spec.ts > Users
Error: Nest can't resolve dependencies of the UserEntityRepository (?). Please make sure that the argument EntityManager at index [0] is available in the MikroOrmModule context.
Potential solutions:
- Is MikroOrmModule a valid NestJS module?
- If EntityManager is a provider, is it part of the current MikroOrmModule?
- If EntityManager is exported from a separate @Module, is that module imported within MikroOrmModule?
@Module({
imports: [ /* the Module containing EntityManager */ ]
})
❯ TestingInjector.lookupComponentInParentModules ../../node_modules/@nestjs/core/injector/injector.js:254:19
❯ TestingInjector.resolveComponentInstance ../../node_modules/@nestjs/core/injector/injector.js:207:33
❯ TestingInjector.resolveComponentInstance ../../node_modules/@nestjs/testing/testing-injector.js:19:45
❯ resolveParam ../../node_modules/@nestjs/core/injector/injector.js:128:38
❯ TestingInjector.resolveConstructorParams ../../node_modules/@nestjs/core/injector/injector.js:143:27
❯ TestingInjector.loadInstance ../../node_modules/@nestjs/core/injector/injector.js:70:13
❯ TestingInjector.loadProvider ../../node_modules/@nestjs/core/injector/injector.js:97:9
❯ ../../node_modules/@nestjs/core/injector/instance-loader.js:56:13
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
Serialized Error: { context: { index: +0, dependencies: [ 'Function<EntityManager>' ], name: 'Function<EntityManager>' }, metadata: { id: 'ea6b9d4dc383116f0dad6' }, moduleRef: { id: 'd1869bbd6934953ea6b9d' }, what: 'Function<what>' }
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/2]⎯
FAIL src/users/users.e2e-spec.ts > Users
TypeError: Cannot read properties of undefined (reading 'close')
❯ src/users/users.e2e-spec.ts:79:15
77|
78| afterAll(async () => {
79| await app.close();
| ^
80| });
81| });
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[2/2]⎯
Test Files 1 failed (1)
Tests (1)
Start at 11:41:22
Duration 2.39s (transform 285ms, setup 0ms, collect 1.32s, tests 18ms, environment 0ms, prepare 313ms)
npm ERR! Lifecycle script `test:e2e` failed with error:
npm ERR! Error: command failed
npm ERR! in workspace: api
npm ERR! at location: /home/me/apps/api
"@mikro-orm/core": "^6.1.6",
"@mikro-orm/nestjs": "^5.2.3",
"@nestjs/core": "^10.0.0",
"@nestjs/platform-fastify": "^10.3.3",
"vitest": "^1.3.1"
当我尝试注释掉
imports: [UsersModule]
时,错误消息发生了变化:Error: Nest could not find PostgreSqlMikroORM element (this provider does not exist in the current context)
。
我知道您正在尝试模拟整个 EntityManager,但由于多种原因它不会起作用。当谈论 E2E 测试而不是单元测试时,NestJS 中的最佳方法是拥有整体视图并将您的应用程序视为在真实环境中运行。如果你看一下官方的 NestJS 课程,那就是他们使用的方法,他们在进行 E2E 时不会模拟数据库或实体管理器之类的东西。
我的建议是忘记模拟数据库连接并使用真实的数据库(即测试数据库)并在“真实环境”中进行测试。 NestJS 课程引用了模拟的优点和缺点,基本上,如果使用模拟方法,它会变得脆弱且难以维护 E2E 测试,因为,例如,如果您模拟 SQL 查询,则无法真正测试您的 SQL 查询是否正确。实体管理器,因为它不会真正执行该查询。
当然,您可能仍然想模拟一些东西,特别是您无法控制的第三方服务或外部服务。例如,向 Stripe 发送请求,这些需要被模拟。