Einführung
Beim Testen von Anwendungen in NestJS sind Mocks ein nützliches Werkzeug, um die Testumgebung von externen Abhängigkeiten zu entkoppeln. Dies erleichtert die Überprüfung der Logik, ohne sich um komplexe Integrationen oder Datenbankverbindungen kümmern zu müssen.
In diesem Beitrag werden wir erweiterte Teststrategien für NestJS-Module und -Dienste mit Mocks besprechen und dabei auch Beispiele zur Implementierung durchgehen.
Vorteile des Mockings
Beim Testen ist es oft notwendig, Abhängigkeiten wie Datenbanken, APIs oder externe Services zu simulieren. Die Verwendung von Mocks bietet mehrere Vorteile:
- Isolation: Tests können unabhängig von echten externen Diensten durchgeführt werden.
- Leistung: Da echte Datenbanken oder APIs nicht verwendet werden, laufen die Tests schneller.
- Vorhersagbarkeit: Mocks liefern festgelegte Antworten, was die Reproduzierbarkeit von Tests verbessert.
Grundlagen der Erstellung von Mocks in NestJS
In NestJS können Mocks auf verschiedene Weise erstellt werden, aber der häufigste Ansatz ist die Verwendung des jest.mock oder die manuelle Bereitstellung von Testwerten. Hier ein einfaches Beispiel:
// foo.service.ts
@Injectable()
export class FooService {
constructor(private readonly barService: BarService) {}
async getFoo(): Promise {
const bar = await this.barService.getBar();
return `Foo says: ${bar}`;
}
}
Angenommen, wir möchten BarService in einem Testfall mocken, ohne den tatsächlichen Service aufzurufen.
// foo.service.spec.ts
describe('FooService', () => {
let fooService: FooService;
let barService: BarService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
FooService,
{
provide: BarService,
useValue: { getBar: jest.fn().mockResolvedValue('mocked Bar') },
},
],
}).compile();
fooService = module.get(FooService);
barService = module.get(BarService);
});
it('should return mocked Bar', async () => {
expect(await fooService.getFoo()).toBe('Foo says: mocked Bar');
});
});
In diesem Beispiel haben wir den BarService durch ein Mock ersetzt, das eine festgelegte Antwort zurückgibt. Dies ermöglicht es uns, die Logik von FooService isoliert zu testen.
Verwendung von jest.spyOn zur Überprüfung von Methodenaufrufen
Mit jest.spyOn können wir Überprüfungen darauf durchführen, ob bestimmte Methoden in unseren Diensten aufgerufen wurden. Dies ist besonders nützlich, wenn wir sicherstellen wollen, dass bestimmte Abhängigkeiten korrekt genutzt werden.
describe('FooService with jest.spyOn', () => {
let fooService: FooService;
let barService: BarService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [FooService, BarService],
}).compile();
fooService = module.get(FooService);
barService = module.get(BarService);
});
it('should call BarService.getBar', async () => {
const spy = jest.spyOn(barService, 'getBar').mockResolvedValue('spied Bar');
await fooService.getFoo();
expect(spy).toHaveBeenCalled();
expect(spy).toHaveBeenCalledWith(); // überprüft, dass keine Argumente übergeben wurden
});
});
In diesem Test wird überprüft, ob die Methode getBar() im BarService tatsächlich aufgerufen wurde. Gleichzeitig liefert der Spy eine gefälschte Antwort zurück.
Mocking von asynchronen Abhängigkeiten
Es ist üblich, dass Dienste in NestJS asynchron sind. Auch in diesen Fällen können Mocks verwendet werden. Hier ein Beispiel:
// bar.service.ts
@Injectable()
export class BarService {
async getBar(): Promise {
return 'Bar';
}
}
// foo.service.spec.ts - Mock für asynchrone Methode
it('should return mocked async Bar', async () => {
jest.spyOn(barService, 'getBar').mockResolvedValue('async mocked Bar');
const result = await fooService.getFoo();
expect(result).toBe('Foo says: async mocked Bar');
});
In diesem Test simulieren wir eine asynchrone Antwort von BarService.getBar(), indem wir mockResolvedValue() verwenden.
Verwendung von Test.createTestingModule() zur Mock-Erstellung
Das Testmodul von NestJS ermöglicht es uns, Testumgebungen zu erstellen und Abhängigkeiten einfach zu mocken. Dies ist besonders nützlich, wenn wir viele Abhängigkeiten in einem Modul haben, aber nur bestimmte mocken wollen.
const module: TestingModule = await Test.createTestingModule({
providers: [
FooService,
{
provide: BarService,
useValue: { getBar: jest.fn().mockResolvedValue('mocked Bar') },
},
AnotherService, // weitere echte Abhängigkeit
],
}).compile();
Hier mocken wir nur BarService, während andere Abhängigkeiten wie AnotherService als echte Implementierungen verwendet werden.
Fazit
Erweiterte Teststrategien mit Mocks in NestJS ermöglichen es uns, unsere Dienste isoliert und effizient zu testen. Durch die Verwendung von Mocks können wir sicherstellen, dass unser Code korrekt funktioniert, ohne externe Abhängigkeiten oder langsame Datenbankverbindungen zu benötigen.
Die vorgestellten Beispiele zeigen, wie wir Mocks mit NestJS und Jest einsetzen können, um Dienste zu isolieren, asynchrone Abhängigkeiten zu testen und Methodenaufrufe zu überwachen. Diese Techniken sind besonders nützlich in größeren Projekten, in denen eine klare Trennung der Logik von ihren Abhängigkeiten unerlässlich ist.
