I want to use an in-memory Mongo instance to mock data for testing purposes in my NestJS application. I have a database provider which connects to my production db using mongoose, which is part of my database module, which in turn gets imported into other modules.
I am trying to override the database provider within my Jest tests so I can use the in-memory Mongo instance.
This is the database module:
import { Module } from '@nestjs/common';
import { databaseProviders } from './database.providers';
@Module({
providers: [...databaseProviders],
exports: [...databaseProviders],
})
export class DatabaseModule { }
and the databaseProvider:
export const databaseProviders = [
{
provide: 'DbConnectionToken',
useFactory: async (): Promise<typeof mongoose> =>
await mongoose.connect(PRODUCTION_DATABASE_URL),
},
];
I have an Events module which imports and uses the database connection from the database module the Events service is what I am testing - the beforeEach in my events.spec.ts:
beforeEach(async () => {
const module = await Test.createTestingModule({
imports: [EventsModule],
providers: [
EventsService,
{
provide: 'EventModelToken',
useValue: EventSchema
},
],
}).compile();
eventService = module.get<EventsService>(EventsService);
});
I tried importing the DatabaseModule into the testing module and then adding my custom provider assuming it would override the database provider, but it doesn't work as I expected so I fear I may misunderstand how overriding providers works in this context.
This is what I tried:
beforeEach(async () => {
const module = await Test.createTestingModule({
imports: [EventsModule, DatabaseModule],
providers: [
EventsService,
{
provide: 'EventModelToken',
useValue: EventSchema
},
{
provide: 'DbConnectionToken',
useFactory: async (): Promise<typeof mongoose> =>
await mongoose.connect(IN_MEMORY_DB_URI),
},
],
}).compile();
eventService = module.get<EventsService>(EventsService);
});
As specified in the docs https://docs.nestjs.com/fundamentals/unit-testing you can override the provider with a value, factory or class.
beforeEach(async () => {
const module = await Test.createTestingModule({
imports: [EventsModule, DatabaseModule],
providers: [
EventsService,
],
}).overrideProvider('DbConnectionToken')
.useFactory({
factory: async (): Promise<typeof mongoose> =>
await mongoose.connect(IN_MEMORY_DB_URI),
})
.compile();
eventService = module.get<EventsService>(EventsService);
});
An alternative would be to make a provider for your configs :) Like so!
@Module({})
export DatabaseModule {
public static forRoot(options: DatabaseOptions): DynamicModule {
return {
providers: [
{
provide: 'DB_OPTIONS',
useValue: options,
},
{
provide: 'DbConnectionToken',
useFactory: async (options): Promise<typeof mongoose> => await mongoose.connect(options),
inject: ['DB_OPTIONS']
},
],
};
}
}
Then use like this
const module: TestingModule = await Test.createTestingModule({
imports: [DatabaseModule.forRoot({ host: 'whatever'})],
});
Now you're able to change the options wherever you're using it :)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With