我使用的是NestJS的mongoose模块,所以我有模式和接口,在服务中,我使用@InjectModel注入模型。我不知道如何模拟要注入服务的模型。
我的服务如下所示:
@Injectable()
export class AuthenticationService {
constructor(@InjectModel('User') private readonly userModel: Model<User>) {}
async createUser(dto: CreateUserDto): Promise<User> {
const model = new this.userModel(dto);
model.activationToken = this.buildActivationToken();
return await model.save();
}
}
Run Code Online (Sandbox Code Playgroud)
在我的服务测试中,我有这个:
const mockMongooseTokens = [
{
provide: getModelToken('User'),
useValue: {},
},
];
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
...mockMongooseTokens,
AuthenticationService,
],
}).compile();
service = module.get<AuthenticationService>(AuthenticationService);
});
Run Code Online (Sandbox Code Playgroud)
但是当我运行测试时,我得到了这个错误:
TypeError: this.userModel is not a constructor
Run Code Online (Sandbox Code Playgroud)
我也想让我的模型对其执行单元测试,如本文所示
小智 12
我知道这篇文章较旧,但如果将来有人应该再次回答这个问题,这里有一个示例,说明如何设置模拟模型并监视任何底层查询调用方法。我花了比我想要的更长的时间来解决这个问题,但这里有一个完整的示例测试,不需要任何额外的工厂功能或任何东西。
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 './models/user.model';
import { UsersRepository } from './users.repository';
import * as CustomScalars from '@common/graphql/scalars/data.scalar';
describe('UsersRepository', () => {
let mockUserModel: Model<UserDocument>;
let mockRepository: UsersRepository;
beforeAll(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
{
provide: getModelToken(User.name),
useValue: Model // <-- Use the Model Class from Mongoose
},
UsersRepository,
...Object.values(CustomScalars),
],
}).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<UsersRepository>(UsersRepository);
});
it('should be defined', () => {
expect(mockRepository).toBeDefined();
});
it('should return a user doc', async () => {
// arrange
const user = new User();
const userID = '12345';
const spy = jest
.spyOn(mockUserModel, 'findById') // <- spy on what you want
.mockResolvedValue(user as UserDocument); // <- Set your resolved value
// act
await mockRepository.findOneById(userID);
// assert
expect(spy).toBeCalled();
});
});
Run Code Online (Sandbox Code Playgroud)
您的问题似乎并未引起很多关注,因为我遇到了相同的问题,因此这是我实施的解决方案。希望它对其他NestJS爱好者有帮助!
您收到的错误消息非常明确:this.userModel确实不是构造函数,因为您向提供了一个空对象useValue。为确保有效注入,useValue必须是的子类mongoose.Model。猫鼬github仓库本身对底层概念给出了一致的解释(来自第63行):
* In Mongoose, the term "Model" refers to subclasses of the `mongoose.Model`
* class. You should not use the `mongoose.Model` class directly. The
* [`mongoose.model()`](./api.html#mongoose_Mongoose-model) and
* [`connection.model()`](./api.html#connection_Connection-model) functions
* create subclasses of `mongoose.Model` as shown below.
Run Code Online (Sandbox Code Playgroud)
换句话说,猫鼬模型是具有尝试连接数据库的几种方法的类。在我们的案例中,唯一使用的Model方法是save()。猫鼬使用javascript构造函数的语法,可以使用相同的语法编写我们的模拟。
模拟应该是带有save()参数的构造函数。
服务测试如下:
* In Mongoose, the term "Model" refers to subclasses of the `mongoose.Model`
* class. You should not use the `mongoose.Model` class directly. The
* [`mongoose.model()`](./api.html#mongoose_Mongoose-model) and
* [`connection.model()`](./api.html#connection_Connection-model) functions
* create subclasses of `mongoose.Model` as shown below.
Run Code Online (Sandbox Code Playgroud)
我还做了一些重构,将所有内容包装在beforeEach块中。save()我为测试选择的实现是一个简单的标识函数,但是可以根据要对的返回值进行断言的方式以不同的方式实现它createUser()。
这种解决方案的一个问题恰恰是您对函数的返回值进行断言,但不能对调用数进行断言,而save()不是a jest.fn()。我找不到module.get在模块范围之外访问模型令牌的方法。如果有人找到方法,请告诉我。
另一个问题是userModel必须在测试的类中创建的实例。findById()例如,当您要测试时,这是有问题的,因为没有实例化模型,但是在集合上调用了该方法。解决方法是new在该useValue级别添加关键字:
beforeEach(async () => {
function mockUserModel(dto: any) {
this.data = dto;
this.save = () => {
return this.data;
};
}
const module = await Test.createTestingModule({
providers: [
AuthenticationService,
{
provide: getModelToken('User'),
useValue: mockUserModel,
},
],
}).compile();
authenticationService = module.get<AuthenticationService>(AuthenticationService);
});
Run Code Online (Sandbox Code Playgroud)
return await不应使用该语法,因为它会引起ts-lint错误(规则:no-return-await)。请参阅相关的github doc问题。
小智 5
为了响应@jbh解决方案,解决在方法调用中未实例化类的问题的一种方法findById()是使用静态方法,您可以这样使用
class mockModel {
constructor(public data?: any) {}
save() {
return this.data;
}
static findOne({ _id }) {
return data;
}
}
mockModel.findOne();
Run Code Online (Sandbox Code Playgroud)
有关静态方法的更多信息:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/static
| 归档时间: |
|
| 查看次数: |
719 次 |
| 最近记录: |