如何用 jest 在服务 (NestJS) 中测试模型 (Mongoose)

Posted

技术标签:

【中文标题】如何用 jest 在服务 (NestJS) 中测试模型 (Mongoose)【英文标题】:How to test Models (Mongoose) in a service (NestJS) with jest 【发布时间】:2020-07-16 19:06:24 【问题描述】:

我有一个用 NestJS 完成的后端。在我的服务中,我注入了两个 Mongoose 模型。我使用 Jest 来测试服务。 模型按原样声明并注入到模块中:

quizes.providers.ts
import  Connection  from 'mongoose';
import  QuizSchema  from './schemas/quiz.schema';

export const quizesProviders = [
  
    provide: 'CLASS_MODEL',
    useFactory: (connection: Connection) => connection.model('Quiz', QuizSchema),
    inject: ['DATABASE_CONNECTION'],
  ,
];

users.providers.ts
import  Connection  from 'mongoose';
import  UserSchema  from './schemas/user.schema';

export const usersProviders = [
  
    provide: 'USER_MODEL',
    useFactory: (connection: Connection) => connection.model('User', UserSchema),
    inject: ['DATABASE_CONNECTION'],
  ,
];

模块示例:

quizes.module.ts
import  Module  from '@nestjs/common';
import  QuizesController  from './quizes.controller';
import  QuizesService  from './quizes.service';

import  quizesProviders  from './quizes.providers';

import  usersProviders  from '../auth/users.providers';

import  DatabaseModule  from 'src/database.module';
import  AuthModule  from 'src/auth/auth.module';

@Module(
  imports: [DatabaseModule, AuthModule],
  controllers: [QuizesController],
  providers: [QuizesService,
  ...quizesProviders, ...usersProviders]
)
export class QuizesModule 

然后在我的服务中,我注入模型:

quizes.service.ts
@Injectable()
export class QuizesService 
    constructor(
        @Inject('CLASS_MODEL')
        private classModel: Model<Quiz>,
        @Inject('USER_MODEL')
        private userModel: Model<User>
      ) 

在我的 quizes.spec.ts(开玩笑)中,我开始做类似的事情。它编译但不起作用:

import  Test  from '@nestjs/testing';
import * as mongoose from 'mongoose';
import  User  from 'src/auth/user.interface';
import  Quiz  from './quiz.interface';
import  databaseProviders  from '../database.providers';

const USER_MODEL:mongoose.Model<User> =  mongoose.model('User', UserSchema);

const CLASS_MODEL:mongoose.Model<Quiz> =  mongoose.model('Quiz', QuizSchema);

const mockingQuizModel = () => 
  find: jest.fn()


const mockingUserModel = () => 
 find: jest.fn()


const mockUser = 
  username: 'Test user'


describe('QuizesService', () => 
    let quizesService;
    let userModel , classModel;


    beforeEach(async () => 
        const module = await Test.createTestingModule(

            providers: [QuizesService, ...usersProviders, ...quizesProviders,...databaseProviders,
            provide: USER_MODEL, useFactory: mockingUserModel,
            provide: CLASS_MODEL, useFactory: mockingQuizModel,
        ],
        ).compile();

        quizesService = await module.get<QuizesService>(QuizesService);
        classModel = await module.get<mongoose.Model<Quiz>>(CLASS_MODEL)
        userModel = await module.get<mongoose.Model<User>>(USER_MODEL)

    )

    describe('getAllQuizes', ()=> 

        it('get all quizes', () => 
           expect(userModel.find).not.toHaveBeenCalled();
        )

    )
)

userModel 未定义,测试未退出。

【问题讨论】:

【参考方案1】:

固定

你不应该对 module.get 使用 await

quizesService = module.get<QuizesService>(QuizesService);
clientClassModel = module.get(getModelToken('CLASS_MODEL'))
clientUserModel =  module.get(getModelToken('USER_MODEL'))

【讨论】:

【参考方案2】:

测试套件的设置是好的,但不是测试 我测试服务 getAllQuizes 方法

这里是服务

@Injectable()
export class QuizesService 
    constructor(
        @InjectModel('CLASS_MODEL')
        private classModel: Model<Quiz>,
        @InjectModel('USER_MODEL')
        private userModel: Model<User>
      ) 

async getAllQuizes(user: User) : Promise<Quiz[]> 
  // console.log(user);
    let userId;
    try 
    const userEntity = await this.userModel.find(username: user.username).exec();
    userId = userEntity[0]._id;
     catch (error) 
      throw new NotFoundException('user not found');

    
    return await this.classModel.find(user: userId).exec();


这是测试

it('get all quizes', async () => 
            clientUserModel.find.mockResolvedValue('user1');

            clientClassModel.find.mockResolvedValue([title: 'test', description: 'test'])

           expect(clientUserModel.find).not.toHaveBeenCalled();
           expect(clientClassModel.find).not.toHaveBeenCalled();
           const result = quizesService.getAllQuizes(mockUser);
           expect(clientUserModel.find).toHaveBeenCalled();
           expect(clientClassModel.find).toHaveBeenCalled();
           expect(result).toEqual([title: 'test', description: 'test']);

        )

我的测试是假的,因为断言 expect(clientClassModel.find).toHaveBeenCalled() 是假的 而在我的服务中,我第一次调用用户模型的 find 方法,第二次调用类模型的 find 方法

【讨论】:

我让它通过测试 NotFoundException【参考方案3】:

终于测试通过了

describe("getAllQuizes", () => 
  it("get all quizes, user not found", async () => 
    clientUserModel.find.mockRejectedValue("user not found");
    clientClassModel.find.mockResolvedValue([
       title: "test", description: "test" ,
    ]);

    expect(clientUserModel.find).not.toHaveBeenCalled();
    expect(clientClassModel.find).not.toHaveBeenCalled();
    const result = quizesService.getAllQuizes(mockUser).catch((err) => 
      expect(err.message).toEqual("user not found");
    );
    expect(clientUserModel.find).toHaveBeenCalled();
  );

  it("get all quizes, find quizzes", async () => 
    clientUserModel.find.mockReturnValue(
      _id: "1234",
      username: "Test user",
    );
    clientClassModel.find.mockResolvedValue([
       title: "test", description: "test" ,
    ]);

    expect(clientUserModel.find).not.toHaveBeenCalled();
    expect(clientClassModel.find).not.toHaveBeenCalled();
    const result = quizesService.getAllQuizes(mockUser).then((state) => 
      expect(clientUserModel.find).toHaveBeenCalled();
      expect(clientClassModel.find).toHaveBeenCalled();
      expect(state).toEqual([ title: "test", description: "test" ]);
    );

    //
  );
);

【讨论】:

【参考方案4】:

使用NestJS官方定义的getModelToken函数:https://docs.nestjs.com/v6/ 技术 -> Mongo(向下滚动到测试部分)

那么你的代码应该看起来像这样:

import  getModelToken  from '@nestjs/mongoose';

const mockRepository = 
    find() 
      return ;
    
  ;

const module = await Test.createTestingModule(    
    providers: [ ...,
    provide: getModelToken('CLASS_MODEL'), useValue: mockRepository,,
    provide: getModelToken('USER_MODEL'), useValue: mockRepository,,
],
...

【讨论】:

以上是关于如何用 jest 在服务 (NestJS) 中测试模型 (Mongoose)的主要内容,如果未能解决你的问题,请参考以下文章

用 Jest 测试 NestJs 服务

如何用 jest 测试 axios 拦截器

在 NestJS 中使用 Jest 和 MongoDB 进行单元测试

如何用 Jest 测试一系列数字?

如何用 jest 测试 combineReducers

如何用 jest 测试 nuxt?