All Projects → davesag → Sequelize Test Helpers

davesag / Sequelize Test Helpers

Licence: mit
A collection of utilities to help with unit-testing Sequelize models

Programming Languages

javascript
184084 projects - #8 most used programming language

Projects that are alternatives of or similar to Sequelize Test Helpers

timelite
String date and time utilities 🕙
Stars: ✭ 17 (-79.52%)
Mutual labels:  utilities, helpers
Alfonz
Mr. Alfonz is here to help you build your Android app, make the development process easier and avoid boilerplate code.
Stars: ✭ 90 (+8.43%)
Mutual labels:  utilities, helpers
utils
🛠 A collection of light-weight methods and helpers for defensive programming
Stars: ✭ 15 (-81.93%)
Mutual labels:  utilities, helpers
Sequelize Mock
A simple mock interface specifically for testing code relying on Sequelize models
Stars: ✭ 116 (+39.76%)
Mutual labels:  sequelize, unit-testing
dpytools
Collection of easy to use, beginner friendly but powerful, orthogonal tools to speed up discord bots development (discord.py)
Stars: ✭ 23 (-72.29%)
Mutual labels:  utilities, helpers
php-lodash
php-lodash is a PHP utility library, similar to Underscore/Lodash.
Stars: ✭ 35 (-57.83%)
Mutual labels:  utilities, helpers
reactools
Create React interfaces is easy.
Stars: ✭ 14 (-83.13%)
Mutual labels:  utilities, helpers
Utils
🛠 Lightweight utilities for string & array manipulation, image handling, safe JSON encoding/decoding, validation, slug or strong password generating etc.
Stars: ✭ 1,158 (+1295.18%)
Mutual labels:  utilities
Useful Tools
A list of useful tools and programs for developers, DevOps and SysAdmins
Stars: ✭ 74 (-10.84%)
Mutual labels:  utilities
C koans
C Koans
Stars: ✭ 65 (-21.69%)
Mutual labels:  unit-testing
Redux Saga Test Plan
Test Redux Saga with an easy plan.
Stars: ✭ 1,135 (+1267.47%)
Mutual labels:  unit-testing
Sequelize Paginate
Sequelize model plugin for add paginate method
Stars: ✭ 70 (-15.66%)
Mutual labels:  sequelize
Sinon
Test spies, stubs and mocks for JavaScript.
Stars: ✭ 8,828 (+10536.14%)
Mutual labels:  unit-testing
Simplestubs
*SimpleStubs* is a simple mocking framework that supports Universal Windows Platform (UWP), .NET Core and .NET framework. SimpleStubs is currently developed and maintained by Microsoft BigPark Studios in Vancouver.
Stars: ✭ 66 (-20.48%)
Mutual labels:  unit-testing
Koa2 Ratelimit
Rate-limiting middleware for Koa2 ES6. Use to limit repeated requests to APIs and/or endpoints such as password reset.
Stars: ✭ 81 (-2.41%)
Mutual labels:  sequelize
Express Sequelize Crud
Simply expose resource CRUD (Create Read Update Delete) routes for Express & Sequelize. Compatible with React Admin Simple Rest Data Provider
Stars: ✭ 65 (-21.69%)
Mutual labels:  sequelize
Sample Code Movies
This repository contains sample code. Its purpose being, to quickly demonstrate Android and software development in general, clean code, best practices, testing and all those other must know goodies.
Stars: ✭ 81 (-2.41%)
Mutual labels:  unit-testing
Aspnetcorespa
Asp.Net 5.0 & Angular 11 SPA Fullstack application with plenty of examples. Live demo:
Stars: ✭ 1,211 (+1359.04%)
Mutual labels:  unit-testing
Memote
memote – the genome-scale metabolic model test suite
Stars: ✭ 73 (-12.05%)
Mutual labels:  unit-testing
Aunit
Unit testing framework for Arduino platforms inspired by ArduinoUnit and Google Test. Used with AUniter or EpoxyDuino for continuous builds.
Stars: ✭ 73 (-12.05%)
Mutual labels:  unit-testing

Horizontal Logo

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

NPM

Related Projects

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.

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'] }
      ]
    }
  )

  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', () => {
    ;['email', 'token'].forEach(checkUniqueIndex(user))
  })
})

Built-in checks

Check What it does
checkHookDefined Checks that a particular hook is defined.
checkModelName Checks that the model is named correctly.
checkNonUniqueIndex Checks that a specific non-unique index is defined.
checkPropertyExists Checks that the model has defined the given property.
checkUniqueCompoundIndex Checks that a specific unique compound index is defined.
checkUniqueIndex Checks that a specific unique index is defined.

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 model-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 model-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

Branch Status Coverage Audit Notes
develop CircleCI codecov Vulnerabilities Work in progress
master CircleCI codecov Vulnerabilities Latest stable release

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

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].