Nestjs unit test: TypeError: this.userModel.findById(...).exec is not a function
Nestjs unit test: TypeError: this.userModel.findById(...).exec is not a function
使用 nestjs 框架和使用猫鼬执行 CRUD 操作的存储库 class 我们有一个简单的 users.repository.ts
文件,如下所示:
@Injectable()
export class UserRepository {
constructor(@InjectModel(User.name) private userModel: Model<UserDocument>) {}
async create(createUserInput: CreateUserInput) {
const createdUser = new this.userModel(createUserInput);
return await createdUser.save();
}
}
async findById(_id: MongooseSchema.Types.ObjectId) {
return await this.userModel.findById(_id).exec();
}
并且在服务器启动时它可以正常工作。
考虑这个 users.repository.spec
文件:
import { Test, TestingModule } from '@nestjs/testing';
import { getModelToken } from '@nestjs/mongoose';
import { Model } from 'mongoose';
// User is my class and UserDocument is my typescript type
// ie. export type UserDocument = User & Document; <-- Mongoose Type
import { User, UserDocument } from '../domain/user.model';
import { UserRepository } from './users.repository';
//import graphqlScalars from 'graphql-scalar-types';
describe('UsersRepository', () => {
let mockUserModel: Model<UserDocument>;
let mockRepository: UserRepository;
beforeAll(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
{
provide: getModelToken(User.name),
useValue: Model, // <-- Use the Model Class from Mongoose
},
UserRepository,
//graphqlScalars,
],
}).compile();
// Make sure to use the correct Document Type for the 'module.get' func
mockUserModel = module.get<Model<UserDocument>>(getModelToken(User.name));
mockRepository = module.get<UserRepository>(UserRepository);
});
it('should be defined', () => {
expect(mockRepository).toBeDefined();
});
it('should return a user doc', async () => {
// arrange
const user = new User();
const userId = user._id;
const spy = jest
.spyOn(mockUserModel, 'findById') // <- spy
.mockResolvedValue(user as UserDocument); // <- set resolved value
// act
await mockRepository.findById(userId);
// assert
expect(spy).toBeCalled();
});
});
所以我的问题是:
对于 should return a user doc
测试,我得到 TypeError: metatype is not a constructor
,我猜
.mockResolvedValue(user as UserDocument);
应该是固定的。
Note:graphql 用于 API 的查询,我不知道是否应该提供标量,如果我取消对标量的注释, expect(mockRepository).toBeDefined();
测试将不再通过
所以任何修复测试的想法都会受到赞赏。
要处理链式 .exec
我们应该通过 mockReturnThis()
:
定义它
static findById = jest.fn().mockReturnThis();
我需要通过 new
调用构造函数,所以我更喜欢以这种方式定义模拟 class:
class UserModelMock {
constructor(private data) {}
new = jest.fn().mockResolvedValue(this.data);
save = jest.fn().mockResolvedValue(this.data);
static find = jest.fn().mockResolvedValue(mockUser());
static create = jest.fn().mockResolvedValue(mockUser());
static remove = jest.fn().mockResolvedValueOnce(true);
static exists = jest.fn().mockResolvedValue(false);
static findOne = jest.fn().mockResolvedValue(mockUser());
static findByIdAndUpdate = jest.fn().mockResolvedValue(mockUser());
static findByIdAndDelete = jest.fn().mockReturnThis();
static exec = jest.fn();
static deleteOne = jest.fn().mockResolvedValue(true);
static findById = jest.fn().mockReturnThis();
}
使用 nestjs 框架和使用猫鼬执行 CRUD 操作的存储库 class 我们有一个简单的 users.repository.ts
文件,如下所示:
@Injectable()
export class UserRepository {
constructor(@InjectModel(User.name) private userModel: Model<UserDocument>) {}
async create(createUserInput: CreateUserInput) {
const createdUser = new this.userModel(createUserInput);
return await createdUser.save();
}
}
async findById(_id: MongooseSchema.Types.ObjectId) {
return await this.userModel.findById(_id).exec();
}
并且在服务器启动时它可以正常工作。
考虑这个 users.repository.spec
文件:
import { Test, TestingModule } from '@nestjs/testing';
import { getModelToken } from '@nestjs/mongoose';
import { Model } from 'mongoose';
// User is my class and UserDocument is my typescript type
// ie. export type UserDocument = User & Document; <-- Mongoose Type
import { User, UserDocument } from '../domain/user.model';
import { UserRepository } from './users.repository';
//import graphqlScalars from 'graphql-scalar-types';
describe('UsersRepository', () => {
let mockUserModel: Model<UserDocument>;
let mockRepository: UserRepository;
beforeAll(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
{
provide: getModelToken(User.name),
useValue: Model, // <-- Use the Model Class from Mongoose
},
UserRepository,
//graphqlScalars,
],
}).compile();
// Make sure to use the correct Document Type for the 'module.get' func
mockUserModel = module.get<Model<UserDocument>>(getModelToken(User.name));
mockRepository = module.get<UserRepository>(UserRepository);
});
it('should be defined', () => {
expect(mockRepository).toBeDefined();
});
it('should return a user doc', async () => {
// arrange
const user = new User();
const userId = user._id;
const spy = jest
.spyOn(mockUserModel, 'findById') // <- spy
.mockResolvedValue(user as UserDocument); // <- set resolved value
// act
await mockRepository.findById(userId);
// assert
expect(spy).toBeCalled();
});
});
所以我的问题是:
对于 should return a user doc
测试,我得到 TypeError: metatype is not a constructor
,我猜
.mockResolvedValue(user as UserDocument);
应该是固定的。
Note:graphql 用于 API 的查询,我不知道是否应该提供标量,如果我取消对标量的注释, expect(mockRepository).toBeDefined();
测试将不再通过
所以任何修复测试的想法都会受到赞赏。
要处理链式 .exec
我们应该通过 mockReturnThis()
:
static findById = jest.fn().mockReturnThis();
我需要通过 new
调用构造函数,所以我更喜欢以这种方式定义模拟 class:
class UserModelMock {
constructor(private data) {}
new = jest.fn().mockResolvedValue(this.data);
save = jest.fn().mockResolvedValue(this.data);
static find = jest.fn().mockResolvedValue(mockUser());
static create = jest.fn().mockResolvedValue(mockUser());
static remove = jest.fn().mockResolvedValueOnce(true);
static exists = jest.fn().mockResolvedValue(false);
static findOne = jest.fn().mockResolvedValue(mockUser());
static findByIdAndUpdate = jest.fn().mockResolvedValue(mockUser());
static findByIdAndDelete = jest.fn().mockReturnThis();
static exec = jest.fn();
static deleteOne = jest.fn().mockResolvedValue(true);
static findById = jest.fn().mockReturnThis();
}