Testing and using NestJS dynamic modules & services using Jest
NestJs is a powerful tool for creating web applications, I've written about it in the past (check the previous posts if you are interested), despite I don't use it in a dailiy basis, I think I can help you to understand how its Dependency Inversion Control works.
I'm going to explain it with a simple example: A simple module which reads client information using an external module which provides a DB connection
Suppose you already have a Module called DatabseModule which declares a provider named DatabaseService, also this Service is exported in the module.
So, you want to use the DatabaseService in another module: UserModule. This module needs two different database connections, here is where dynamic module injection via params appears.
Module & Service preparation
import { Injectable, Inject } from '@nestjs/common';
import PouchDB from 'pouchdb';
@Injectable()
export class PouchService {
private remotedb: PouchDB.Database
constructor(@Inject('DATABASE') private database) {
this.remotedb = new PouchDB('http://endpoint.com/' + database)
}
}
With the prevous snippet we've created a simple DatabaseService wich its constructor awaits a string, the database name. Because we're using pouchDB in the example, we need to stablish a new connection for each database we want to use. (PouchDB is a NO-SQL database).
The interesting part in the code is the @Inject annotation, with it, NestJS injects the property 'DATABASE' into the constructor.
For the second part, we need to provide that value, we will do it in the top level, in the Module itself. The way for making this module more flexible, is to create a static method called register() or forRoot() which has the database string as parameter, and then return the DatabaseModule with the DatabaseService properly configured for the given database:
import { Module, DynamicModule } from '@nestjs/common';
import { PouchService } from './pouch/pouch.service';
@Module({
providers: [PouchService]
})
export class DatabaseModule {
static register(database: string): DynamicModule {
return {
module: null,
providers: [
{
provide: 'DATABASE',
useValue: database
},
PouchService
],
exports: []
}
}
}
Now let's code the test scaffold for implementing Jest tests
The trick is so simple, we need to modify the createTestingModule() function and specify the parameter we want to initialize the service to be tested:
import { Test, TestingModule } from '@nestjs/testing';
import { PouchService } from './pouch.service';
describe('PouchService', () => {
let service: PouchService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
{
provide: PouchService,
useValue: new PouchService('tenants'),
}
],
}).compile();
service = module.get<PouchService>(PouchService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
it('should return info', async () => {
const info = await service.info()
expect(info).toBeDefined()
})
});