So, I have this Nest.js API app that use SWC and Vitest. Here are the minimized files of Users module.
CRUD users service:
// 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 file:
// 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 file:
// users.module.ts
...
@Module({
controllers: [UsersController],
exports: [UsersService],
imports: [MikroOrmModule.forFeature([UserEntity])],
providers: [UsersService],
})
export class UsersModule {}
E2E testing file:
// 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();
});
});
And I got the error below when I run 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
Versions
"@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"
When I tried commenting out imports: [UsersModule], error message changed: Error: Nest could not find PostgreSqlMikroORM element (this provider does not exist in the current context).
I understand that you are trying to mock the entire EntityManager, but it is not going to work for many reasons. When talking about E2E testing, not Unit Tests, the best approach in NestJS is to have a holistic view and treat your application as if it was running in a real environment. If you take a look a the official NestJS course that's the approach they use, they don't mock the database or things like the entity manager when doing E2E.
My recommendation is to forget about mocking the database connection and use a real database (i.e. a test database) and do your tests in a "real environment". The NestJS course quoted the pros and cons of mocking and basically it comes down to having fragile and hard to maintain E2E tests if using the mocked approach because, for example, you can't really test if your SQL query is correct if you mock the entity manager as it will not really execute that query.
Of course, you still may want to mock some stuff, specially third party services that you do not control or external services. i.e. sending a request to Stripe for example, those needs to be mocked.