sequelize / sequelize-auto

Automatically generate bare sequelize models from your database.
2.9k stars 527 forks source link

Mocking auto generated model functions #560

Open cmgchess opened 2 years ago

cmgchess commented 2 years ago

More of a question than an issue

I have created my models using sequelize-auto 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 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,

//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()

Any help would be greatly appreciated Thanks in advance

steveschmitt commented 2 years ago

I don't know how Sequelize attaches the findAll function to the models. Let me know if you figure this one out.

cmgchess commented 2 years ago

@steveschmitt Thanks for the reply!

I couldn't figure out a proper way of doing this 😕

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);

Fixed my issue for now until I come across a better solution.

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 []

Would like to know if there is a better way

steveschmitt commented 2 years ago

I don't know why separating it to a different file would make a difference. You must be doing something different in the new code, or in a different order. I'm glad you got it working.

mia-pelino commented 1 year ago

I finally figured this one out for my jest unit tests that needed to conditionally mock the resolve values for sequelize auto generated model functions:

import { models } from '../data-store';

const mockAccountFindOne = jest.fn();
const mockItemCreate = jest.fn();
jest.mock('../data-store', () => ({
  models: {
    Account: {
      findOne: jest.fn(() => mockAccountFindOne()),
    },
    Item: {
      create: jest.fn(() => mockItemFindAll()),
    },
  },
}));
sujeet-agrahari commented 1 year ago

I finally figured this one out for my jest unit tests that needed to conditionally mock the resolve values for sequelize auto generated model functions:

import { models } from '../data-store';

const mockAccountFindOne = jest.fn();
const mockItemCreate = jest.fn();
jest.mock('../data-store', () => ({
  models: {
    Account: {
      findOne: jest.fn(() => mockAccountFindOne()),
    },
    Item: {
      create: jest.fn(() => mockItemFindAll()),
    },
  },
}));

I am doing the same as below:

import { vi } from 'vitest';
const models = require('../../../src/db/models');
vi.mock('../../../src/db/models', () => ({
  User: {
    findOne: vi.fn(),
  },
}));

But it still tries to make the connection in the actual code where User.findOne({..} is used.