Defining and Importing Models

Defining and importing Sequelize models follows a very predictable pattern.

You write a Model definition file such as:

src/models/Simple.js

const model = (sequelize, DataTypes) => {

const Simple = sequelize.define('Simple', {

name: DataTypes.STRING,

email: DataTypes.STRING

}) return Simple

} module.exports = model

and add an index file src/models/index.js

const fs = require('fs')

const path = require('path')

const Sequelize = require('sequelize') const {

dbName,

dbUser,

dbPass,

options

} = require('../utils/dbConfig') const basename = path.basename(module.filename)

const sequelize = new Sequelize(dbName, dbUser, dbPass, options)

const db = { Sequelize, sequelize } const onlyModels = file =>

file.indexOf('.') !== 0 &&

file !== basename &&

file.slice(-3) === '.js' const importModel = file => {

const modelPath = path.join(__dirname, file)

const model = sequelize.import(modelPath)

db[model.name] = model

} const associate = modelName => {

if (typeof db[modelName].associate === 'function')

db[modelName].associate(db)

} fs.readdirSync(__dirname)

.filter(onlyModels)

.forEach(importModel) Object.keys(db).forEach(associate) module.exports = db

This index.js file:

creates a connection to the database, walks through the local folder and invokes sequelize.import on each model file, stores the associated model by its name , then for each model with an associate function, invokes that function to wire the models together.

This is pretty much how all projects that use Sequelize work to pre-load their models from individual model files.

Unit-Testing Models

As soon as your code first calls require('src/models') the line

const sequelize = new Sequelize(dbName, dbUser, dbPass, options)

gets invoked, and that’s going to attempt to make a connection to your database. This is both slow, and, if you don’t actually have a database running, likely to cause connection errors.

In our tests we therefore want to completely avoid having to require('src/models') at all. We can of focus the test on the specific model we want to test:

const SimpleModel = require('src/models/Simple')

But for that to be of use we need to pass it an instantiated sequelize object, and a Sequelize DataTypes object.

The sequelize-test-helpers library provides a mock sequelize instance and a complete set of sequelize data types.

Unit-Testing a Model’s Name and Properties

test/unit/models/Simple.spec.js

const {

sequelize,

dataTypes,

checkModelName,

checkPropertyExists

} = require('sequelize-test-helpers') const SimpleModel = require('../../../src/models/Simple') describe('src/models/Simple', () => {

const Model = SimpleModel(sequelize, dataTypes)

const instance = new Model() checkModelName(Model)('Simple') context('properties', () => {

;['name', 'email'].forEach(checkPropertyExists(instance))

})

})

This tests that the model’s name is the name we expect, and that the model has the properties we expect.

Unit-Testing Associations

The sequelize-test-helpers library also provides utilities to allow us to test for associations.

Let’s just imagine that our Simple model above is associated with an OtherModel .

The mock sequelize object uses sinon to attach spies to each of Sequelize’s association types, meaning you can test them by invoking Model.associate in a before block, then using sinon ’s calledWith function to check that it was invoked correctly.

context('check associations', () => {

const OtherModel = 'some other model' // it doesn't matter what before(() => {

Model.associate({ OtherModel })

} it('defined a belongsTo association with OtherModel', () => {

expect(Simple.belongsTo).to.have.been.calledWith(OtherModel)

})

}

You can do this for all of belongsTo , hasOne , hasMany , and belongsToMany .

Unit-Testing Hooks

Often you’ll define hooks in a model, such as beforeValidate , or afterCreate , and Sequelize provides three different ways to define those hooks. Here’s a simple, and ridiculous, model that defines three hooks, each is defined in a different way.

src/models/HasHooks.js

const model = (sequelize, DataTypes) => {

const HasHooks = sequelize.define(

'HasHooks',

{

name: DataTypes.STRING

},

{

hooks: {

beforeValidate: hooker => {

hooker.name = 'Alice'

}

}

}

) HasHooks.hook('afterValidate', hooker => {

hooker.name = 'Bob'

}) HasHooks.addHook('afterCreate', 'removeMe', hooker => {

hooker.name = 'Carla'

}) return HasHooks

} module.exports = model

The sequelize-test-helpers library provides a helper to allow us to test for hooks, no matter how they are defined (or how stupid they are.)

const {

sequelize,

dataTypes,

checkHookDefined

} = require('sequelize-test-helpers') const HasHooksModel = require('../../src/models/HasHooks') describe('src/models/HasHooks', () => {

const Model = HasHooksModel(sequelize, dataTypes)

const instance = new Model() context('hooks', () => {

;[

'beforeValidate',

'afterValidate',

'afterCreate'

].forEach(checkHookDefined(instance))

})

})

Unit-Testing Indexes

You can use the various check*Index functions to check for a range of different index types.

context('indexes', () => {

;['email', 'token'].forEach(checkUniqueIndex(instance))

})

You can check for the following index types:

checkUniqueIndex : Checks that a specific unique index is defined,

: Checks that a specific is defined, checkNonUniqueIndex : Checks that a specific non-unique index is defined, and

: Checks that a specific is defined, and checkUniqueCompoundIndex : Checks that a specific unique compound index is defined.