Jest测试报错:TypeError: Article is not a constructor 求助
问题
为article.service.js中的saveArticle函数编写Jest单元测试时,触发错误:TypeError: Article is not a constructor,无法定位原因。
项目中article.model.js基于Mongoose Schema定义了Article模型并导出,article.service.js通过解构引入后用new Article()创建实例。测试文件尝试mock Article模型,但执行测试时报错。测试目标是验证slugify和currentUser函数是否按预期调用。
相关代码如下:
article.model.js
const mongoose = require('mongoose'); const Schema = mongoose.Schema; const articleSchema = new Schema( { slug: { type: String }, title: { type: String, required: true }, description: { type: String, required: true }, body: { type: String, required: true }, tagList: [String], favoritesCount: { type: Number, default: 0 }, comments: [{ type: mongoose.Types.ObjectId, ref: 'Comment' }], author: { type: mongoose.Types.ObjectId, ref: 'User' }, }, { timestamps: true } ); const Article = mongoose.model('Article', articleSchema); module.exports.Article = Article;
article.service.js
const { Article } = require('../models/article.model'); const { currentUser } = require('../services/user.service'); const { slugify } = require('../helpers/slugify.helper'); class ArticleService { static async saveArticle(userId, articleObj) { const newArticle = new Article(); // ...more code here... } }
article.service.test.js
const slugifyHelper = require('../../../helpers/slugify.helper'); const currentUserService = require('../../../services/user.service'); const { Article } = require('../../../models/article.model'); const mockUser = { _id: "6368ad4347dkhs", email: "test@gmail.com" }; const mockArticle = { title: "Sample article title", description: "Sample article description", body: "Sample article body", tagList: ['tag1', 'tag2'] }; // Mock the Article model jest.mock('../../../models/article.model', () => ({ save: jest.fn().mockResolvedValue(mockUser) })); const slugifySpy = jest.spyOn(slugifyHelper, "slugify").mockReturnValue('mocked-slug'); const currentUserSpy = jest.spyOn(currentUserService, "currentUser").mockResolvedValue(mockUser); describe("Unit tests for article.service.js file", () => { beforeEach(() => { jest.clearAllMocks(); }); describe("saveArticle tests", () => { test("If user can save an article", async () => { //Arrange const { saveArticle } = require("../../../services/article.service"); //Act const result = await saveArticle(mockUser._id, mockArticle); //Assert expect(Article).toHaveBeenCalledTimes(1); expect(slugifySpy).toHaveBeenCalledTimes(1); expect(slugifySpy).toHaveBeenCalledWith(mockArticle.title); expect(currentUserSpy).toHaveBeenCalledTimes(1); expect(currentUserSpy).toHaveBeenCalledWith(mockUser._id); }); }); });
解决方案
错误原因
你在jest.mock中模拟的模块返回值结构错误:原模块导出的是包含Article构造函数的对象,但你的mock返回的是带save方法的普通对象,没有提供Article构造函数,导致new Article()调用失败。
修复步骤
修正mock的模块结构
调整jest.mock返回值,匹配原模块的导出结构,把Article模拟成可实例化的构造函数,并为实例添加save方法:jest.mock('../../../models/article.model', () => ({ Article: jest.fn().mockImplementation(() => ({ save: jest.fn().mockResolvedValue({ ...mockArticle, _id: 'mock-id', slug: 'mocked-slug' }) })) }));调整断言逻辑(可选)
若需要验证实例的save方法调用,可添加以下断言:const mockArticleInstance = Article.mock.results[0].value; expect(mockArticleInstance.save).toHaveBeenCalledTimes(1);优化模块引入时机
建议先完成mock,再引入目标服务和依赖,避免模块提前加载导致mock失效:// 先执行mock jest.mock('../../../models/article.model', () => ({ Article: jest.fn().mockImplementation(() => ({ save: jest.fn().mockResolvedValue({ ...mockArticle, _id: 'mock-id', slug: 'mocked-slug' }) })) })); // 再引入相关模块 const slugifyHelper = require('../../../helpers/slugify.helper'); const currentUserService = require('../../../services/user.service'); const { Article } = require('../../../models/article.model'); const { saveArticle } = require("../../../services/article.service");
完整修复后的测试文件示例
const slugifyHelper = require('../../../helpers/slugify.helper'); const currentUserService = require('../../../services/user.service'); const mockUser = { _id: "6368ad4347dkhs", email: "test@gmail.com" }; const mockArticle = { title: "Sample article title", description: "Sample article description", body: "Sample article body", tagList: ['tag1', 'tag2'] }; // 修正后的mock jest.mock('../../../models/article.model', () => ({ Article: jest.fn().mockImplementation(() => ({ save: jest.fn().mockResolvedValue({ ...mockArticle, _id: 'mock-article-id', slug: 'mocked-slug', author: mockUser._id }) })) })); // 引入mock后的模块和服务 const { Article } = require('../../../models/article.model'); const { saveArticle } = require("../../../services/article.service"); const slugifySpy = jest.spyOn(slugifyHelper, "slugify").mockReturnValue('mocked-slug'); const currentUserSpy = jest.spyOn(currentUserService, "currentUser").mockResolvedValue(mockUser); describe("Unit tests for article.service.js file", () => { beforeEach(() => { jest.clearAllMocks(); }); describe("saveArticle tests", () => { test("If user can save an article", async () => { //Act const result = await saveArticle(mockUser._id, mockArticle); //Assert expect(Article).toHaveBeenCalledTimes(1); // 断言实例的save方法被调用 const articleInstance = Article.mock.results[0].value; expect(articleInstance.save).toHaveBeenCalledTimes(1); expect(slugifySpy).toHaveBeenCalledTimes(1); expect(slugifySpy).toHaveBeenCalledWith(mockArticle.title); expect(currentUserSpy).toHaveBeenCalledTimes(1); expect(currentUserSpy).toHaveBeenCalledWith(mockUser._id); }); }); });
内容的提问来源于stack exchange,提问作者Nilesh Sanyal
相关产品推荐
相关产品推荐

