I want to write a unit test for my nestjs ‘Course’ repository service (a service that has dependencies on Mongoose Model and Redis).
courses.repository.ts:
import { Injectable, HttpException, NotFoundException } from "@nestjs/common";
import { InjectModel } from "@nestjs/mongoose"
import { Course } from "../../../../shared/course";
import { Model } from "mongoose";
import { RedisService } from 'nestjs-redis';
@Injectable({})
export class CoursesRepository {
private redisClient;
constructor(
@InjectModel('Course') private courseModel: Model<Course>,
private readonly redisService: RedisService,
) {
this.redisClient = this.redisService.getClient();
}
async findAll(): Promise<Course[]> {
const courses = await this.redisClient.get('allCourses');
if (!courses) {
console.log('return from DB');
const mongoCourses = await this.courseModel.find();
await this.redisClient.set('allCourses', JSON.stringify(mongoCourses), 'EX', 20);
return mongoCourses;
}
console.log('return from cache');
return JSON.parse(courses);
}
}
The test is initialized this way:
beforeEach(async () => {
const moduleRef = await Test.createTestingModule({
imports: [
MongooseModule.forRoot(MONGO_CONNECTION, {
useNewUrlParser: true,
useUnifiedTopology: true
}),
MongooseModule.forFeature([
{ name: "Course", schema: CoursesSchema },
{ name: "Lesson", schema: LessonsSchema }
]),
RedisModule.register({})
],
controllers: [CoursesController, LessonsController],
providers: [
CoursesRepository,
LessonsRepository
],
}).compile();
coursesRepository = moduleRef.get<CoursesRepository>(CoursesRepository);
redisClient = moduleRef.get<RedisModule>(RedisModule);
});
My Course repository service has 2 dependencies – Redis and Mongoose Model (Course).
I would like to mock both of them.
If I was mocking a provider I would use that syntax:
providers: [
{provide: CoursesRepository, useFactory: mockCoursesRepository},
LessonsRepository
],
Can I create a mock Redis service which will be used instead of the an actual Redis service during a test?
How ?
Thanks,
Yaron
2
Answers
You can mock your
RedisService
just as any other dependency. Since you are really interested in the Redis client and not the service, you have to create an intermediate mock for the service. For mongoose, you need thegetModelToken
method for getting the correct injection token, see this answer:Please also note, that you probably should not import modules in unit tests (unless it is a testing module). See this answer on a distinction between unit and e2e tests.
How to create mocks?
See this answer.
Note that the accepted answer mocks a provider, which will satisfy the dependencies of your class under test IF it is also supplied from the
TestingModule
with a provider. However, it’s not mockingimport
s as asked, which can be different. E.g. if you need toimport
some module which also depends on the mocked value, this will NOT work:To mock an
import
, you can use aDynamicModule
that exports the mocked values:As that’s a bit verbose, a utility function such as this can be helpful:
Which can then be used like:
This often has the added benefit of letting you
import
the module for the class under test, rather than having the test bypass the module andprovide
the value directly.