Search code examples
expressunit-testingjestjssequelize.js

Express Jest problem running test with Sequelize


Problem

While trying to run some Jest testing on my Express application, I encounter this error:

FAIL models/tests/user.model.test.js ● Test suite failed to run

TypeError: Sequelize is not a constructor

  2 | const path = require('path');
  3 |
> 4 | const sequelize = new Sequelize({
    |                   ^
  5 |     dialect: 'sqlite',
  6 |     storage: path.join(__dirname, 'database.sqlite'),
  7 |     logging: console.log // Output logs to the console

  at Object.<anonymous> (models/connection.js:4:19)     
  at Object.require (models/user.model.js:2:19)
  at Object.require (models/__tests__/user.model.test.js:3:64)

Code

This is the actual code for my Sequelize connection:

const Sequelize = require('sequelize'); // also tried const { Sequelize }
const path = require('path');

const sequelize = new Sequelize({
    dialect: 'sqlite',
    storage: path.join(__dirname, 'database.sqlite'),
    logging: console.log // Output logs to the console
});

sequelize.authenticate()
    .then(() => {
        console.log('Database connection established successfully');
    })
    .catch((error) => {
        console.error('Error connecting to the database: ', error);
    });

module.exports = sequelize;

Then this is the code for my actual tests im running on user.model.js:

const { DataTypes } = require('sequelize');
const bcrypt = require('bcrypt');
const { User, createUser, findUserById, findUserByUsername } = 
require('../user.model.js');

jest.mock('sequelize', () => ({
    define: jest.fn(),
    sync: jest.fn(),
    findOne: jest.fn(),
    create: jest.fn(),
    query: jest.fn(),
}));

jest.mock('bcrypt', () => ({
    hash: jest.fn(),
}));

describe('User Model', () => {
    beforeEach(() => {
        jest.clearAllMocks();
    });

test('Should define User model', () => {
    expect(sequelize.define).toHaveBeenCalledTimes(1);
    expect(sequelize.define).toHaveBeenCalledWith('users', {
        id: {
            type: DataTypes.INTEGER,
            primaryKey: true,
            autoIncrement: true,
          },
          username: {
            type: DataTypes.STRING,
            allowNull: false,
            unique: true,
          },
          password: {
            type: DataTypes.STRING,
            allowNull: false,
          },
    });
});

test('Should create user and hash password', async () => {
    bcrypt.hash.mockResolvedValue('hashedPassword');

    const username = 'testuser';
    const password = 'testpassword';

    const expectedUser = {
        id: 1,
        username,
        password: 'hashedPassword',
    };

    User.create.mockResolvedValue(expectedUser);

    const user = await createUser(username, password);

    expect(User.create).toHaveBeenCalledTimes(1);
    expect(User.create).toHaveBeenCalledWith({
        username,
        password: 'hashedPassword',
    });

    expect(user).toEqual(expectedUser);
    expect(bcrypt.hash).toHaveBeenCalledWith(password, 10);
});

});

Inside my user.model.js file that im running tests on, I import the connection module with const sequelize = require('./connection.js');. I only run into the problem when I try and run my test, if I start the server it will start no problem and I can interact with the database.

What I've Tried

I've asked chatGPT it said to make sure that I have the right dependencies, and to make sure I import sequelize properly in the connection module. I've tried multiple different ways of importing sequelize, but same issue. I know the right dependencies are installed because my other tests work.


Solution

  • Sequelize is a class exported by sequelize module. So you need to mock the class like this: jest.fn(() => instance).

    After mocking, the Sequelize class will become jest.fn(() => instance),

    new Sequelize(/**/) will return the instance.

    You should use Mocking Partials, which means we just want to mock the Sequelize class and keep using the original implementation of other things.

    import Sequelize, { DataTypes } from 'sequelize';
    
    jest.mock('sequelize', () => {
      return {
        ...jest.requireActual('sequelize'),
        default: jest.fn(() => ({
          define: jest.fn(),
          sync: jest.fn(),
          findOne: jest.fn(),
          create: jest.fn(),
          query: jest.fn(),
        })),
        __esModule: true,
      };
    });
    
    describe('76657256', () => {
      test('should pass', () => {
        expect(jest.isMockFunction(Sequelize)).toBeTruthy();
        expect(DataTypes.INTEGER).toBeDefined();
      });
    });