Reputation: 1446
I've been getting this error all day long:
Nest can't resolve dependencies of the ClubsService (ClubsApiService, AuthApiService, ClubFollowersRepo, ClubsRepo, ClubPrivacyRepo, ?). Please make sure that the argument DatabaseConnection at index [5] is available in the ClubsModule context.
Potential solutions:
- If DatabaseConnection is a provider, is it part of the current ClubsModule?
- If DatabaseConnection is exported from a separate @Module, is that module imported within ClubsModule?
@Module({
imports: [ /* the Module containing DatabaseConnection */ ]
})
I figured that the problem is, that I have not mocked the Mongo DB connection. The error is quite clear, the @InjectConnection
in ClubsService
should be mocked (see below).
ClubsService:
@Injectable()
export class ClubsService {
constructor(
private readonly clubsApiService: ClubsApiService,
private readonly authApiService: AuthApiService,
private readonly clubFollowersRepo: ClubFollowersRepo,
private readonly clubsRepo: ClubsRepo,
private readonly clubPrivacyRepo: ClubPrivacyRepo,
@InjectConnection() private readonly connection: Connection, // <--- THIS GUY
) {}
// ...
}
The problem is that the test file I am executing is in a different module than where ClubsService
is. And so in the different module (let's call it YModule
), I have this piece of code:
YModule:
import { getConnectionToken } from '@nestjs/mongoose';
import { MongoMemoryServer } from 'mongodb-memory-server';
import { Connection, connect } from 'mongoose';
describe('YService.spec.ts in YModule', () => {
beforeAll(async () => {
mongod = await MongoMemoryServer.create();
const uri = mongod.getUri();
mongoConnection = (await connect(uri)).connection;
});
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
// ...
],
imports: [ClubsModule], // <--- ClubsModule is not provider, but imported module
})
.overrideProvider(getConnectionToken())
.useValue(mongoConnection)
.compile();
});
});
This approach with getConnectionToken()
won't work as I have to mock a connection coming from the imported ClubsModule
, not a provider.
How would you mock a connection injected in a different module that you imported?
Thanks a lot! :)
Upvotes: 2
Views: 1457
Reputation: 1446
As Jay McDoniel mentioned in the post comment, you should not import modules in your unit testing file but mock the needed dependencies instead. Why is that? Consider the example from the question above:
ClubsModule
has the connection dependency that can be replaced with an in-memory database server, that's true, but this should be done within the ClubsModule
itself (clubs
folder), not outside in different modules.
What you really want to do outside ClubsModule
, let's say, in the YModule
(y
folder), is to mock every provider ClubsModule
exports that you use within the test file of YModule
.
This makes sense as you should test ClubsModule
specific dependencies only within its module and everywhere else just mock it.
I originally imported ClubsModule
because I wanted to use the repository (a provider) that ClubsModule
exports. But then I realized that I don't want to test the functionality of the repository's function, I already test them inside the ClubsModule
, so there is no need to do that twice. Instead, it is a good idea to mock the repository instead.
Code Example:
y.service.spec.ts
:
import { YService } from './y.service'; // <--- For illustration; Provider within YModule
import { ClubsRepo } from '../clubs/clubs.repo'; // <--- Import the real Repository Provider from different Module (ClubsModule)
describe('y.service.spec.ts in YModule', () => {
const clubsRepo = { // <--- Mocking ClubRepo's functions used within this test file
insertMany: () => Promise.resolve([]),
deleteMany: () => Promise.resolve(),
}
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
ClubsRepo, // <--- The real Repository Provider from the import statement above
],
})
.overrideProvider(ClubsRepo) // <--- Overriding the Repository Provider from imports
.useValue(clubsRepo) // <--- Overriding to the mock 'clubsRepo' (const above)
.compile();
service = module.get<YService>(YService); // <--- For illustration; unlike ClubsRepo, this provider resides within this module
});
it('example', () => {
// ...
jest.spyOn(clubsRepo, 'insertMany'); // <--- Using "insertMany" from the mocked clubsRepo (the const) defined at the beginning
// ...
});
});
The reason for importing ClubsRepo
to the test file y.service.spec.ts
is because y.service.ts
(the actual Provider in YModule
) uses the functions of the ClubsRepo
. In this case, don't forget importing ClubsModule
in y.module.ts
too.
y.module.ts
:
import { ClubsModule } from '../clubs/clubs.module';
@Module({
imports: [
// ...
ClubsModule, // <--- Don't forget this line
// ...
],
providers: [
// ...
],
})
export class YModule {}
That's it, happy testing! :)
Upvotes: 2