'Mocking auto generated model functions created using sequelize-auto package

I have created my models using the sequelize-auto package and used them in my controllers

const sequelize = require('../database/db');
var models = require("../models/init-models").initModels(sequelize);

var User = models.User;

const controllerMethod = async (req,res,next) => {
    //calls User.findAll() and returns the results
}

I have called the findAll function of User model in one of my controller methods

I want to test my controller method using Jest and want to mock the findAll function to return an empty object. I have imported my models in the test file and mocked the findAll function as follows,

//inside test case
            models.User.findAll = jest.fn().mockImplementation(() => {
                return {}
            });
            const spy = jest.spyOn(models.User, "findAll")
            await controllerMethod(req, res,next);

My question is when I run the test case it runs the actual findAll() function inside the controller instead of the mocked findAll()
i.e. findAll() returns actual data instead of {}

Any help would be greatly appreciated
Thanks in advance



Solution 1:[1]

Welcome to Stack Overflow. I think the issue with your code is some confusion on how spyOn works. Please see the documentation here specifically the following:

Note: By default, jest.spyOn also calls the spied method. This is different behavior from most other test libraries. If you want to overwrite the original function, you can use jest.spyOn(object, methodName).mockImplementation(() => customImplementation) or object[methodName] = jest.fn(() => customImplementation);

What this is telling you is that spyOn actual calls the original method, not the mock implementation.

The way I would do this (and assuming you do not need to assert on how findAll is called) is not use spyOn at all but create a dummy models that is returned from initModels, which has your dummy findAll method on it. Something like the following:

const mockModels = {
        User: {
            findAll: jest.fn(() => {})
        }
    };
    // And then in your test - be careful as jest.mock is "hoisted" so you need to make sure mockModels has already been declared and assigned

test('blah', () => {
    jest.mock("../models/init-models", () => ({
        initModels: jest.fn(() => mockModels,
    });
    await controllerMethod(req, res, next) // ...etc

Solution 2:[2]

Managed to fix my issue until I come across a better solution.

I created a seperate models.js file and exported all my Models via that. Imported Models to my Controllers from the models.js file instead of const sequelize = require('../database/db'); var models = require("../models/init-models").initModels(sequelize);

models.js

const sequelize = require('../database/db');
var models = require("./init-models").initModels(sequelize);

module.exports.User= models.User;
module.exports.Instrument = models.Instrument;

module.exports.sequelize = sequelize;  //exported this too since I have used sequelize.fn in some of my controllers

userController.js

//const sequelize = require('../database/db');
//var models = require("../models/init-models").initModels(sequelize);

//var User = models.User;
const {User,sequelize} = require('../service/models');  //imported models this way

const controllerMethod = async (req,res,next) => {
    //calls await User.findAll() and returns the results
}

userController.test.js

const {controllerMethod} = require('../../../controllers/user');
const {User,sequelize} = require('../../../service/models');

//inside test case
           jest.spyOn(User, "findAll").mockImplementation(() => {return Promise.resolve([])});
           await controllerMethod(req, res,next);

in this way findAll mocks the way I wanted and returns the expected []

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Marcus
Solution 2 cmgchess