Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

sequelize-test-helpers

Package Overview
Dependencies
Maintainers
1
Versions
27
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

sequelize-test-helpers

A collection of utilities to help with unit-testing sequelize models

  • 1.4.3
  • latest
  • Source
  • npm
  • Socket score

Version published
Weekly downloads
11K
decreased by-3.86%
Maintainers
1
Weekly downloads
 
Created
Source

Horizontal Logo

A collection of utilities to help with unit-testing Sequelize models and code that needs those models.

NPM

How to use

Prerequisites

This library assumes:

  1. You are using chai — Version 4 or better.
  2. You are using sinon — Version 5 or better.
  3. Using mocha is also recommended, but as long as you are using chai and sinon this should work with any test runner.

Note Jest is not supported unless you are also using sinon and chai, which is unlikely.

Installation

Add sequelize-test-helpers as a devDependency:

npm i -D sequelize-test-helpers

Examples

Unit testing models created with sequelize.define

Note: See below for how to test models created using Model.init

Let's say you have a Sequelize model User as follows:

src/models/User.js
const model = (sequelize, DataTypes) => {
  const User = sequelize.define(
    'User',
    {
      age: {
        type: DataTypes.INTEGER.UNSIGNED
      },
      firstName: {
        type: DataTypes.STRING,
        allowNull: false,
        validate: {
          notEmpty: true
        }
      },
      lastName: {
        type: DataTypes.STRING,
        allowNull: false,
        validate: {
          notEmpty: true
        }
      },
      email: {
        type: DataTypes.STRING,
        allowNull: false,
        unique: true,
        lowercase: true,
        validate: {
          isEmail: true,
          notEmpty: true
        }
      },
      token: {
        type: DataTypes.STRING,
        validate: {
          notEmpty: true
        }
      }
    },
    {
      indexes: [
        { unique: true, fields: ['email'] },
        { unique: true, fields: ['token'] },
        { unique: false, fields: ['firstName', 'lastName'] }
      ]
    }
  )

  User.associate = ({ Company }) => {
    User.belongsTo(Company)
  }

  return User
}

module.exports = model

You can use sequelize-test-helpers to unit-test this with mocha as follows:

test/unit/models/User.spec.js
const { expect } = require('chai')

const {
  sequelize,
  dataTypes,
  checkModelName,
  checkUniqueIndex,
  checkPropertyExists
} = require('sequelize-test-helpers')

const UserModel = require('../../src/models/User')

describe('src/models/User', () => {
  const User = UserModel(sequelize, dataTypes)
  const user = new User()

  checkModelName(User)('User')

  context('properties', () => {
    ;['age', 'firstName', 'lastName', 'email', 'token'].forEach(checkPropertyExists(user))
  })

  context('associations', () => {
    const Company = 'some dummy company'

    before(() => {
      User.associate({ Company })
    })

    it('defined a belongsTo association with Company', () => {
      expect(User.belongsTo).to.have.been.calledWith(Company)
    })
  })

  context('indexes', () => {
    context('unique', () => {
      ;['email', 'token'].forEach(checkUniqueIndex(user))
    })

    context('non unique (and also composite in this example)', () => {
      ;[['firstName', 'lastName']].forEach(checkNonUniqueIndex(user))
    })
  })
})

Built-in checks

CheckWhat it does
checkHookDefinedChecks that a particular hook is defined.
checkModelNameChecks that the model is named correctly.
checkNonUniqueIndexChecks that a specific non-unique index is defined.
checkPropertyExistsChecks that the model has defined the given property.
checkUniqueIndexChecks that a specific unique index is defined.
Deprecation notice
CheckNote
checkUniqueCompoundIndexUse either checkUniqueIndex or checkNonUniqueIndex

Checking associations

The various association functions are stubbed so you can simply invoke the the model's associate function in a before block then use sinon's standard expectation syntax to check they were called with the correct values.

hasOne
it("defined a hasOne association with Image as 'profilePic'", () => {
  expect(User.hasOne).to.have.been.calledWith(Image, {
    as: 'profilePic'
  })
})
belongsTo
it('defined a belongsTo association with Company', () => {
  expect(User.belongsTo).to.have.been.calledWith(Company)
})
hasMany
it("defined a hasMany association with User as 'employees'", () => {
  expect(Company.hasMany).to.have.been.calledWith(User, {
    as: 'employees'
  })
})
belongsToMany
it("defined a belongsToMany association with Category through CategoriesCompanies as 'categories'", () => {
  expect(Company.belongsToMany).to.have.been.calledWith(Category, {
    through: CategoriesCompanies,
    as: 'categories'
  })
})

Unit testing code that requires models

Let's say you have a utility function that takes some data and uses it to update a user record. If the user does not exist it returns null. (Yes I know this is a contrived example)

src/utils/save.js
const { User } = require('../models')

const save = async ({ id, ...data }) => {
  const user = await User.findOne({ where: { id } })
  if (user) return await user.update(data)
  return null
}

module.exports = save

You want to unit-test this without invoking a database connection (so you can't require('src/models') in your test).

This is where makeMockModels, sinon, and proxyquire come in handy.

test/unit/utils/save.test.js
const { expect } = require('chai')
const { match, stub, resetHistory } = require('sinon')
const proxyquire = require('proxyquire')

const { makeMockModels } = require('sequelize-test-helpers')

describe('src/utils/save', () => {
  const User = { findOne: stub() }
  const mockModels = makeMockModels({ User })

  const save = proxyquire('../../../src/utils/save', {
    '../models': mockModels
  })

  const id = 1
  const data = {
    firstName: 'Testy',
    lastName: 'McTestFace',
    email: 'testy.mctestface.test.tes',
    token: 'some-token'
  }
  const fakeUser = { id, ...data, update: stub() }

  let result

  context('user does not exist', () => {
    before(async () => {
      User.findOne.resolves(undefined)
      result = await save({ id, ...data })
    })

    after(resetHistory)

    it('called User.findOne', () => {
      expect(User.findOne).to.have.been.calledWith(match({ where: { id } }))
    })

    it("didn't call user.update", () => {
      expect(fakeUser.update).not.to.have.been.called
    })

    it('returned null', () => {
      expect(result).to.be.null
    })
  })

  context('user exists', () => {
    before(async () => {
      fakeUser.update.resolves(fakeUser)
      User.findOne.resolves(fakeUser)
      result = await save({ id, ...data })
    })

    after(resetHistory)

    it('called User.findOne', () => {
      expect(User.findOne).to.have.been.calledWith(match({ where: { id } }))
    })

    it('called user.update', () => {
      expect(fakeUser.update).to.have.been.calledWith(match(data))
    })

    it('returned the user', () => {
      expect(result).to.deep.equal(fakeUser)
    })
  })
})

As a convenience, makeMockModels will automatically populate your mockModels with mocks of all of the models defined in your src/models folder (or if you have a .sequelizerc file it will look for the models-path in that). Simply override any of the specific models you need to do stuff with.

Testing models created with Model.init

Sequelize also allows you to create models by extending Sequelize.Model and invoking its static init function as follows:

Note: creating your models this way makes it harder to test their use.

const { Model, DataTypes } = require('sequelize')

const factory = sequelize => {
  class User extends Model {}
  User.init(
    {
      firstName: DataTypes.STRING,
      lastName: DataTypes.STRING
    },
    { sequelize, modelName: 'User' }
  )
  return User
}

module.exports = factory

You can test this using sequelize-test-helpers, sinon, and proxyquire.

const { expect } = require('chai')
const { spy } = require('sinon')
const proxyquire = require('proxyquire')
const { sequelize, Sequelize } = require('sequelize-test-helpers')

describe('src/models/User', () => {
  const { DataTypes } = Sequelize

  const UserFactory = proxyquire('src/models/User', {
    sequelize: Sequelize
  })

  let User

  before(() => {
    User = UserFactory(sequelize)
  })

  // It's important you do this
  after(() => {
    User.init.resetHistory()
  })

  it('called User.init with the correct parameters', () => {
    expect(User.init).to.have.been.calledWith(
      {
        firstName: DataTypes.STRING,
        lastName: DataTypes.STRING
      },
      {
        sequelize,
        modelName: 'User'
      }
    )
  })
})

Listing your models

Assuming your src/models/index.js (or your equivalent) exports all your models, it's useful to be able to generate a list of their names.

const { listModels } = require('sequelize-test-helpers')

console.log(listModels()) // will spit out a list of your model names.

Similarly to makeMockModels above, listModels will find all of the models defined in your src/models folder (or if you have a .sequelizerc file it will look for the models-path in that).

Custom models paths and custom file suffixes

By default makeMockModels and listModels will both look for your models in files ending with .js in either the models path defined in .sequelizerc, or in src/models. If however your models are not .js files and the models folder is somewhere else you can pass in a custom models folder path and a custom suffix.

  • listModels(customModelsFolder, customSuffix)

    const modelNames = listModels('models', '.ts')
    
  • makeMockModels(yourCustomModels, customModelsFolder, customSuffix)

    const models = makeMockModels({ User: { findOne: stub() } }, 'models', '.ts')
    

Development

Branches

BranchStatusCoverageAuditNotes
developCircleCIcodecovVulnerabilitiesWork in progress
mainCircleCIcodecovVulnerabilitiesLatest stable release

Development Prerequisites

  • NodeJS. I use nvm to manage Node versions — brew install nvm.

Initialisation

npm install

Test it

  • npm test — runs the unit tests
  • npm run test:unit:cov — runs the unit tests with code coverage

Lint it

npm run lint

Contributing

Please see the contributing notes.

Thanks

Keywords

FAQs

Package last updated on 30 Jul 2022

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc