TypeError:在使用 Jests 和 Mongoose 测试 NestJS API 时,updatedService.save 不是一个函数
Posted
技术标签:
【中文标题】TypeError:在使用 Jests 和 Mongoose 测试 NestJS API 时,updatedService.save 不是一个函数【英文标题】:TypeError: updatedService.save is not a function when using testing NestJS API with Jests and Mongoose 【发布时间】:2020-08-07 00:02:08 【问题描述】:根据用户输入的 DTO,可以使用HTTP PATCH
更新产品信息。我的 NestJS 服务如下:
async updateAProduct(product: ProductDTO )
const updatedProduct = await this.findProduct(product.id);
if (product.title)
updatedProduct.title = product.title;
if (product.description)
updatedProduct.description = product.description;
if (product.price)
updatedProduct.price = product.price;
updatedProduct.save()
ProductDTO
是一个接口:
export interface ProductDTO
id?: string;
title?: string;
description?: string;
price?: number;
updatedProduct
是从 findProduct
返回的 Mongoose 文档 (ProductDoc
):
import Document from 'mongoose';
export interface ProductDoc extends Document
id: string;
title: string;
description: string;
price: number;
updateAProduct
服务在控制器中调用如下:
@Patch('/update/:id')
async updateAProduct(@Param('id') id: string, @Body() product: ProductDTO)
product.id = id;
await this.productService.updateAProduct(product);
return null;
在写products.service.spec.ts
时,我写了以下测试:
describe('ProductsService', () =>
let service: ProductsService;
let model: Model<ProductDoc>;
beforeEach(async () =>
const module: TestingModule = await Test.createTestingModule(
providers: [
ProductsService,
provide: getModelToken('Product'),
useValue:
new: jest.fn().mockResolvedValue(mockProduct()),
constructor: jest.fn().mockResolvedValue(mockProduct()),
findById: jest.fn(),
find: jest.fn(),
findOne: jest.fn(),
update: jest.fn(),
create: jest.fn(),
remove: jest.fn(),
exec: jest.fn(),
,
,
],
).compile();
service = module.get<ProductsService>(ProductsService);
model = module.get<Model<ProductDoc>>(getModelToken('Product'));
);
it('should update a product sucessfully', async() =>
jest.spyOn(model, 'findById').mockReturnValue(
exec: jest.fn().mockResolvedValueOnce(
mockProductDoc(id: 'uuid1', title: 'Title1', description: 'Description1', price: 50.99)
),
as any);
const updatedProduct = await service.updateAProduct(
id: 'uuid1',
title: 'New Title',
price: 200.00
);
expect(updatedProduct).toEqual(mockProduct('uuid1', 'New Title', 'Description1',200.00));
);
我的测试失败如下:
FAIL src/products/products.service.spec.ts (18.693s)
● ProductsService › should update a product sucessfully
TypeError: updatedProduct.save is not a function
49 | updatedProduct.price = product.price;
50 |
> 51 | updatedProduct.save()
| ^
52 |
53 |
54 | async deleteAProduct(prodID: string)
at ProductsService.updateAProduct (products/products.service.ts:51:24)
如何克服 Jest 测试中不可用的.save()
?
来源:
-
我只是在复制Academind NestJS + MongoDB Tutorial
由于教程中没有测试,我完全依赖 Repository Here jmcdo29/testing-nestjs
编辑
findProduct
服务内
private async findProduct(productID: string): Promise<ProductDoc>
let product;
try
product = await this.productModel.findById(productID).exec();
catch(error)
throw new NotFoundException('Could Not Find Product for given ID.');
if (!product)
throw new NotFoundException('Could Not Find Product for given ID.');
return product;
【问题讨论】:
你模拟的findProduct()
必须返回一个像...mockedObject, save: jest.fn()
这样的对象。我希望你明白吗?!
刚刚意识到。我现在正在尝试。
@MoazzamArif 没有,即使我添加了save: jest.fn()
这个应该可以,但是findProduct(product.id)
这个是怎么实现的呢?
@MoazzamArif 我用findProduct
编辑了这个问题
【参考方案1】:
我的团队上个月遇到了同样的错误!
在搜索了最佳实践之后,我找到了一种简单的方法......
我建议使用 *.repository.ts
文件,这样您就可以简单地将所有 Mongoose 的东西移动到该文件中,而让您的 *.service.spec.ts
更加简单和解耦。所以这个错误永远不会再发生了。
看看这个例子:
product.repository.ts
想法是将所有Mongoose操作放入存储库文件中,例如update()、delete()、find()、populate()、aggregate()、save()...
@Injectable()
export class ProductRepository
constructor(@InjectModel('Product') private readonly model: Model<Product>)
async findProduct(id: string): Promise<Product>
return await this.model.findOne(_id: id).exec();
async save(doc: any): Promise<Product>
return await new this.model(doc).save();
product.service.ts
这里不要使用@InjectModel
,而是注入ProductRepository。我们的服务文件应该尽可能精简,并且只包含业务逻辑。
@Injectable()
export class ProductService
constructor(private readonly repository: ProductRepository)
async updateAProduct(product: ProductDTO)
const updatedProduct = await this.repository.findProduct(product.id);
if (product.title)
updatedProduct.title = product.title;
if (product.description)
updatedProduct.description = product.description;
if (product.price)
updatedProduct.price = product.price;
await this.repository.save(updatedProduct);
product.module.ts
确保您在providers中有ProductRepository
。
@Module(
imports: [MongooseModule.forFeature([ name: 'Product', schema: ProductSchema ])],
controllers: [ProductController],
providers: [ProductService, ProductRepository],
exports: [ProductService],
)
export class ProductModule
product.service.spec.ts
** 而不是使用getModelToken('Product')
替换为ProductRepository
。
const mockProductRepository =
findProduct: jest.fn(),
save: jest.fn(),
;
describe('ProductService', () =>
let service: ProductService;
beforeAll(async () =>
const module: TestingModule = await Test.createTestingModule(
providers: [
ProductService,
provide: ProductRepository,
useValue: mockProductRepository,
],
).compile();
service = module.get<ProductService>(ProductService);
);
describe('Update a product', () =>
it('should update a product sucessfully', async () =>
const findProductStub = id: 'uuid1', title: 'Title1', description: 'Description1', price: 50.99;
mockProductRepository.findProduct.mockResolvedValue(findProductStub);
const saveProductStub = id: 'uuid1', title: 'New Title', description: 'Description1', price: 200.00;
mockProductRepository.save.mockResolvedValue(saveProductStub);
const productToUpdateDto = id: 'uuid1', title: 'New Title', description: 'Description1', price: 200.00;
const result = await service.updateAProduct(productToUpdateDto);
expect(result).toEqual(id: 'uuid1', title: 'New Title', description: 'Description1', price: 200.00);
);
);
);
希望我能帮助你交配!
【讨论】:
这是一个很好的解决方案。您是否经常在存储库中转移其他 Mongoose CRUD 功能,例如create()
和 find().exec()
?
是的,想法是将所有Mongoose操作放入存储库文件中,例如updateOne()
、deleteOne()
、find()
、findOne()
、findById()
、populate()
、@987654339 @、save()
等等……我们的服务文件应该尽可能精简,并且只包含业务逻辑。谢谢老哥!
我建议使用 mockingoose
来模拟 db CRUD。它还将在模拟返回值时保留猫鼬模式以上是关于TypeError:在使用 Jests 和 Mongoose 测试 NestJS API 时,updatedService.save 不是一个函数的主要内容,如果未能解决你的问题,请参考以下文章
如何在sql中将dd-mm-yyyy转换为DD-MON-yyyy