使用Jest测试Node.js Sequelize模型时spyOn save方法报非函数错误
问题根因
你遇到的两个报错本质是同一个问题触发的:Sequelize的save是模型实例方法,不属于模型类的静态方法,你直接对模型类执行spy、直接调用模型类的自定义方法都会找不到对应函数。
Cannot spy the save property because it is not a function; undefined given instead是因为你spy的对象是模型类,类上没有save静态方法。
删除spy后报TypeError: this.save is not a function是因为你调用generateToken时用了模型类直接调用,方法内部的this指向模型类而非实例,自然找不到实例方法save。
修复步骤
- 修正自定义方法的定义,确保是实例方法
如果用class继承的方式定义模型,不要给generateToken加static修饰符:
const { Model } = require('sequelize') class 你的模型类名 extends Model { // 不加static就是实例方法,this指向当前模型实例 generateToken() { return generateTokenHash(20) .then((token) => { this.token = token; return this.save(); }); } } // 后续初始化模型代码省略
如果用旧版sequelize.define的方式定义模型,要把方法挂到模型原型上:
const 你的模型 = sequelize.define('模型名', { // 字段定义省略 }) // 挂载实例方法 你的模型.prototype.generateToken = function() { return generateTokenHash(20) .then((token) => { this.token = token; return this.save(); }); }
- 修正测试代码,操作模型实例而非模型类
测试时先创建模型实例,对实例的save方法进行spy:
const 你的模型 = require('/path/to/your/model') describe('你的测试分组名', () => { it('你的测试用例名', async () => { // 创建模型实例,build方法不会真实写入数据库,适合单元测试 const modelInstance = 你的模型.build({ // 补充模型必填字段的默认值即可 }) // 对实例的save方法做spy,根据需要可以mock返回值避免真实操作数据库 const saveSpy = jest.spyOn(modelInstance, 'save').mockResolvedValue(modelInstance) // 调用实例的generateToken方法 await modelInstance.generateToken() // 这里补充你自己的断言逻辑,比如: expect(saveSpy).toHaveBeenCalledTimes(1) expect(modelInstance.token).not.toBeUndefined() }) })
可选优化
如果不需要测试数据库写入逻辑,可以在spy时直接mocksave的返回,避免测试依赖数据库环境,提升测试运行速度。
内容的提问来源于stack exchange,提问作者styopdev
相关产品推荐
相关产品推荐

