node.js - NestJS Falskning av injicerad anslutning importerad från annat modul
Jag har fått detta fel hela dagen:
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 */ ]
})
Jag kom fram till att problemet är att jag inte har mockat Mongo DB-anslutningen. Felet är ganska tydligt, @InjectConnection i ClubsService bör mockas (se nedan).
Klubbtjänst :
@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
) {}
// ...
}
Problemet är att testfilen jag kör befinner sig i en annan modul än där ClubsService är. Och så i den andra modulen (låtom oss kalla den YModule), har jag denna kodsnutt:
YModul :
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();
});
});
Detta tillvägagångssätt med getConnectionToken() kommer inte att fungera eftersom jag måste mocka en anslutning som kommer från den importerade ClubsModule, inte en leverantör.
Hur skulle du imitera en anslutning som injicerats i en annan modul som du importerat?
Tack så mycket! :)
Svar #1
Som Jay McDoniel nämnde i inläggets kommentar bör du inte importera moduler i din enhetstestfil utan istället mocka de nödvändiga beroenden. Varför är det så? Tänk på exemplet från frågan ovan:
ClubsModule har ett anslutningsberoende som kan ersättas med en in-memory-databasserver, det är sant, men detta bör göras inom själva ClubsModule (clubs-mappen), inte utanför i olika moduler.
Vad du verkligen vill göra utanför ClubsModule, låt oss säga, i YModule (y mapp), är att mocka varje provider som ClubsModule exporterar som du använder inom testfilen för YModule.
Detta är vettigt eftersom du bara bör testa ClubsModule specifika beroenden inom dess modul och överallt annars bara mocka det.
Jag importerade ursprungligen ClubsModule eftersom jag ville använda repositoriet (en leverantör) som ClubsModule exporterar. Men sedan insåg jag att jag inte vill testa funktionaliteten hos repository-funktionen, jag testar dem redan inom ClubsModule, så det finns inget behov av att göra det två gånger. Istället är det en bra idé att mocka repositoriet istället.
Kodexempel:
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
// ...
});
});
Anledningen till att importera ClubsRepo till testfilen y.service.spec.ts är för att y.service.ts (den faktiska Providern i YModule) använder funktionerna i ClubsRepo. I detta fall, glöm inte att även importera ClubsModule i y.module.ts.
y.module.ts:
import { ClubsModule } from '../clubs/clubs.module';
@Module({
imports: [
// ...
ClubsModule, // <--- Don't forget this line
// ...
],
providers: [
// ...
],
})
export class YModule {}
Det är allt, glad testning! :)
