{"id":13473467,"url":"https://github.com/davesag/sequelize-test-helpers","last_synced_at":"2025-04-05T00:10:45.718Z","repository":{"id":38418310,"uuid":"138247592","full_name":"davesag/sequelize-test-helpers","owner":"davesag","description":"A collection of utilities to help with unit-testing Sequelize models","archived":false,"fork":false,"pushed_at":"2024-09-13T08:25:57.000Z","size":2625,"stargazers_count":124,"open_issues_count":3,"forks_count":21,"subscribers_count":5,"default_branch":"develop","last_synced_at":"2024-10-30T06:32:21.486Z","etag":null,"topics":["helpers","mocks","sequelize","sequelizejs","unit-testing","utilities"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/sequelize-test-helpers","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/davesag.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":["davesag"]}},"created_at":"2018-06-22T02:58:06.000Z","updated_at":"2024-10-13T12:16:20.000Z","dependencies_parsed_at":"2023-02-10T01:30:37.099Z","dependency_job_id":"e956dc72-41b5-48af-8a20-c623dfd4220e","html_url":"https://github.com/davesag/sequelize-test-helpers","commit_stats":{"total_commits":385,"total_committers":10,"mean_commits":38.5,"dds":"0.12207792207792212","last_synced_commit":"6d3bca94ae9c76d3628c85d2a747a9b7bae877b4"},"previous_names":[],"tags_count":28,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davesag%2Fsequelize-test-helpers","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davesag%2Fsequelize-test-helpers/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davesag%2Fsequelize-test-helpers/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davesag%2Fsequelize-test-helpers/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/davesag","download_url":"https://codeload.github.com/davesag/sequelize-test-helpers/tar.gz/refs/heads/develop","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247266565,"owners_count":20910836,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["helpers","mocks","sequelize","sequelizejs","unit-testing","utilities"],"created_at":"2024-07-31T16:01:03.853Z","updated_at":"2025-04-05T00:10:45.698Z","avatar_url":"https://github.com/davesag.png","language":"JavaScript","funding_links":["https://github.com/sponsors/davesag"],"categories":["JavaScript"],"sub_categories":[],"readme":"![Horizontal Logo](logo/horizontal.svg)\n\nA collection of utilities to help with unit-testing [Sequelize](http://docs.sequelizejs.com) models and code that needs those models.\n\n[![NPM](https://nodei.co/npm/sequelize-test-helpers.png)](https://nodei.co/npm/sequelize-test-helpers/)\n\n## Related Projects\n\n- [`sequelize-pg-utilities`](https://github.com/davesag/sequelize-pg-utilities) — Simple utilities that help you manage your Sequelize configuration.\n\n## How to use\n\n### Prerequisites\n\nThis library assumes:\n\n1. You are using [`chai`](http://www.chaijs.com) — Version 4 or better.\n2. You are using [`sinon`](http://sinonjs.org) — Version 5 or better.\n3. Using [`mocha`](https://mochajs.org) is also recommended, but as long as you are using `chai` and `sinon` this should work with any test runner.\n\n**Note** Jest is not supported unless you are also using `sinon` and `chai`, which is unlikely.\n\n### Installation\n\nAdd `sequelize-test-helpers` as a `devDependency`:\n\n```sh\nnpm i -D sequelize-test-helpers\n```\n\n## Examples\n\n### Unit testing models created with `sequelize.define`\n\n**Note**: See below for how to test models created using `Model.init`\n\nLet's say you have a Sequelize model `User` as follows:\n\n#### `src/models/User.js`\n\n```js\nconst model = (sequelize, DataTypes) =\u003e {\n  const User = sequelize.define(\n    'User',\n    {\n      age: {\n        type: DataTypes.INTEGER.UNSIGNED\n      },\n      firstName: {\n        type: DataTypes.STRING,\n        allowNull: false,\n        validate: {\n          notEmpty: true\n        }\n      },\n      lastName: {\n        type: DataTypes.STRING,\n        allowNull: false,\n        validate: {\n          notEmpty: true\n        }\n      },\n      email: {\n        type: DataTypes.STRING,\n        allowNull: false,\n        unique: true,\n        lowercase: true,\n        validate: {\n          isEmail: true,\n          notEmpty: true\n        }\n      },\n      token: {\n        type: DataTypes.STRING,\n        validate: {\n          notEmpty: true\n        }\n      }\n    },\n    {\n      indexes: [\n        { unique: true, fields: ['email'] },\n        { unique: true, fields: ['token'] },\n        { unique: false, fields: ['firstName', 'lastName'] }\n      ]\n    }\n  )\n\n  User.associate = ({ Company }) =\u003e {\n    User.belongsTo(Company)\n  }\n\n  return User\n}\n\nmodule.exports = model\n```\n\nYou can use `sequelize-test-helpers` to unit-test this with `mocha` as follows:\n\n#### `test/unit/models/User.spec.js`\n\n```js\nconst { expect } = require('chai')\n\nconst {\n  sequelize,\n  dataTypes,\n  checkModelName,\n  checkUniqueIndex,\n  checkPropertyExists\n} = require('sequelize-test-helpers')\n\nconst UserModel = require('../../src/models/User')\n\ndescribe('src/models/User', () =\u003e {\n  const User = UserModel(sequelize, dataTypes)\n  const user = new User()\n\n  checkModelName(User)('User')\n\n  context('properties', () =\u003e {\n    ;['age', 'firstName', 'lastName', 'email', 'token'].forEach(checkPropertyExists(user))\n  })\n\n  context('associations', () =\u003e {\n    const Company = 'some dummy company'\n\n    before(() =\u003e {\n      User.associate({ Company })\n    })\n\n    it('defined a belongsTo association with Company', () =\u003e {\n      expect(User.belongsTo).to.have.been.calledWith(Company)\n    })\n  })\n\n  context('indexes', () =\u003e {\n    context('unique', () =\u003e {\n      ;['email', 'token'].forEach(checkUniqueIndex(user))\n    })\n\n    context('non unique (and also composite in this example)', () =\u003e {\n      ;[['firstName', 'lastName']].forEach(checkNonUniqueIndex(user))\n    })\n  })\n})\n```\n\n### Built-in checks\n\n| Check                 | What it does                                          |\n| --------------------- | ----------------------------------------------------- |\n| `checkHookDefined`    | Checks that a particular hook is defined.             |\n| `checkModelName`      | Checks that the model is named correctly.             |\n| `checkNonUniqueIndex` | Checks that a specific non-unique index is defined.   |\n| `checkPropertyExists` | Checks that the model has defined the given property. |\n| `checkUniqueIndex`    | Checks that a specific unique index is defined.       |\n\n#### Deprecation notice\n\n| Check                      | Note                                                   |\n| -------------------------- | ------------------------------------------------------ |\n| `checkUniqueCompoundIndex` | Use either `checkUniqueIndex` or `checkNonUniqueIndex` |\n\n### Checking associations\n\nThe 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.\n\n#### `hasOne`\n\n```js\nit(\"defined a hasOne association with Image as 'profilePic'\", () =\u003e {\n  expect(User.hasOne).to.have.been.calledWith(Image, {\n    as: 'profilePic'\n  })\n})\n```\n\n#### `belongsTo`\n\n```js\nit('defined a belongsTo association with Company', () =\u003e {\n  expect(User.belongsTo).to.have.been.calledWith(Company)\n})\n```\n\n#### `hasMany`\n\n```js\nit(\"defined a hasMany association with User as 'employees'\", () =\u003e {\n  expect(Company.hasMany).to.have.been.calledWith(User, {\n    as: 'employees'\n  })\n})\n```\n\n#### `belongsToMany`\n\n```js\nit(\"defined a belongsToMany association with Category through CategoriesCompanies as 'categories'\", () =\u003e {\n  expect(Company.belongsToMany).to.have.been.calledWith(Category, {\n    through: CategoriesCompanies,\n    as: 'categories'\n  })\n})\n```\n\n### Unit testing code that requires `models`\n\nLet'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)\n\n#### `src/utils/save.js`\n\n```js\nconst { User } = require('../models')\n\nconst save = async ({ id, ...data }) =\u003e {\n  const user = await User.findOne({ where: { id } })\n  if (user) return await user.update(data)\n  return null\n}\n\nmodule.exports = save\n```\n\nYou want to unit-test this without invoking a database connection (so you can't `require('src/models')` in your test).\n\nThis is where `makeMockModels`, `sinon`, and [`proxyquire`](https://github.com/thlorenz/proxyquire) come in handy.\n\n#### `test/unit/utils/save.test.js`\n\n```js\nconst { expect } = require('chai')\nconst { match, stub, resetHistory } = require('sinon')\nconst proxyquire = require('proxyquire')\n\nconst { makeMockModels } = require('sequelize-test-helpers')\n\ndescribe('src/utils/save', () =\u003e {\n  const User = { findOne: stub() }\n  const mockModels = makeMockModels({ User })\n\n  const save = proxyquire('../../../src/utils/save', {\n    '../models': mockModels\n  })\n\n  const id = 1\n  const data = {\n    firstName: 'Testy',\n    lastName: 'McTestFace',\n    email: 'testy.mctestface.test.tes',\n    token: 'some-token'\n  }\n  const fakeUser = { id, ...data, update: stub() }\n\n  let result\n\n  context('user does not exist', () =\u003e {\n    before(async () =\u003e {\n      User.findOne.resolves(undefined)\n      result = await save({ id, ...data })\n    })\n\n    after(resetHistory)\n\n    it('called User.findOne', () =\u003e {\n      expect(User.findOne).to.have.been.calledWith(match({ where: { id } }))\n    })\n\n    it(\"didn't call user.update\", () =\u003e {\n      expect(fakeUser.update).not.to.have.been.called\n    })\n\n    it('returned null', () =\u003e {\n      expect(result).to.be.null\n    })\n  })\n\n  context('user exists', () =\u003e {\n    before(async () =\u003e {\n      fakeUser.update.resolves(fakeUser)\n      User.findOne.resolves(fakeUser)\n      result = await save({ id, ...data })\n    })\n\n    after(resetHistory)\n\n    it('called User.findOne', () =\u003e {\n      expect(User.findOne).to.have.been.calledWith(match({ where: { id } }))\n    })\n\n    it('called user.update', () =\u003e {\n      expect(fakeUser.update).to.have.been.calledWith(match(data))\n    })\n\n    it('returned the user', () =\u003e {\n      expect(result).to.deep.equal(fakeUser)\n    })\n  })\n})\n```\n\nAs 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.\n\n### Testing models created with `Model.init`\n\nSequelize also allows you to create models by extending `Sequelize.Model` and invoking its static `init` function as follows:\n\n**Note**: creating your models this way makes it harder to test their use.\n\n```js\nconst { Model, DataTypes } = require('sequelize')\n\nconst factory = sequelize =\u003e {\n  class User extends Model {}\n  User.init(\n    {\n      firstName: DataTypes.STRING,\n      lastName: DataTypes.STRING\n    },\n    { sequelize, modelName: 'User' }\n  )\n  return User\n}\n\nmodule.exports = factory\n```\n\nYou can test this using `sequelize-test-helpers`, `sinon`, and `proxyquire`.\n\n```js\nconst { expect } = require('chai')\nconst { spy } = require('sinon')\nconst proxyquire = require('proxyquire')\nconst { sequelize, Sequelize } = require('sequelize-test-helpers')\n\ndescribe('src/models/User', () =\u003e {\n  const { DataTypes } = Sequelize\n\n  const UserFactory = proxyquire('src/models/User', {\n    sequelize: Sequelize\n  })\n\n  let User\n\n  before(() =\u003e {\n    User = UserFactory(sequelize)\n  })\n\n  // It's important you do this\n  after(() =\u003e {\n    User.init.resetHistory()\n  })\n\n  it('called User.init with the correct parameters', () =\u003e {\n    expect(User.init).to.have.been.calledWith(\n      {\n        firstName: DataTypes.STRING,\n        lastName: DataTypes.STRING\n      },\n      {\n        sequelize,\n        modelName: 'User'\n      }\n    )\n  })\n})\n```\n\n### Listing your models\n\nAssuming 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.\n\n```js\nconst { listModels } = require('sequelize-test-helpers')\n\nconsole.log(listModels()) // will spit out a list of your model names.\n```\n\nSimilarly 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).\n\n## Custom `models` paths and custom file suffixes\n\nBy 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.\n\n- `listModels(customModelsFolder, customSuffix)`\n\n  ```js\n  const modelNames = listModels('models', '.ts')\n  ```\n\n- `makeMockModels(yourCustomModels, customModelsFolder, customSuffix)`\n\n  ```js\n  const models = makeMockModels({ User: { findOne: stub() } }, 'models', '.ts')\n  ```\n\n## Development\n\n### Branches\n\n\u003c!-- prettier-ignore --\u003e\n| Branch | Status | Coverage | Audit | Notes |\n| ------ | ------ | -------- | ----- | ----- |\n| `develop` | [![CircleCI](https://circleci.com/gh/davesag/sequelize-test-helpers/tree/develop.svg?style=svg)](https://circleci.com/gh/davesag/sequelize-test-helpers/tree/develop) | [![codecov](https://codecov.io/gh/davesag/sequelize-test-helpers/branch/develop/graph/badge.svg)](https://codecov.io/gh/davesag/sequelize-test-helpers) | [![Vulnerabilities](https://snyk.io/test/github/davesag/sequelize-test-helpers/develop/badge.svg)](https://snyk.io/test/github/davesag/sequelize-test-helpers/develop) | Work in progress |\n| `main` | [![CircleCI](https://circleci.com/gh/davesag/sequelize-test-helpers/tree/main.svg?style=svg)](https://circleci.com/gh/davesag/sequelize-test-helpers/tree/main) | [![codecov](https://codecov.io/gh/davesag/sequelize-test-helpers/branch/main/graph/badge.svg)](https://codecov.io/gh/davesag/sequelize-test-helpers) | [![Vulnerabilities](https://snyk.io/test/github/davesag/sequelize-test-helpers/main/badge.svg)](https://snyk.io/test/github/davesag/sequelize-test-helpers/main) | Latest stable release |\n\n### Development Prerequisites\n\n- [NodeJS](https://nodejs.org). I use [`nvm`](https://github.com/creationix/nvm) to manage Node versions — `brew install nvm`.\n\n### Initialisation\n\n```sh\nnpm install\n```\n\n### Test it\n\n- `npm test` — runs the unit tests\n- `npm run test:unit:cov` — runs the unit tests with code coverage\n\n### Lint it\n\n```sh\nnpm run lint\n```\n\n## Contributing\n\nPlease see the [contributing notes](CONTRIBUTING.md).\n\n## Thanks\n\n- Thanks to [`reallinfo`](https://github.com/reallinfo) for the logo.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdavesag%2Fsequelize-test-helpers","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdavesag%2Fsequelize-test-helpers","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdavesag%2Fsequelize-test-helpers/lists"}