A guide to writing testable Sequelize models for Nodejs apps

While writing a data-driven app about a month ago, I hit a wall. A few days later — and after doing a lot of reading on the web, I figured out how to get past it. As a humble internet citizen (or so I think) I’ve decided to share my experience so here we go.

A typical data-driven app has a few database tables that may have relationships between them. An extremely simplified example is a player that collects items in a game.

A little game app

We can represent this with a bunch of classes in Sequelize.

First, we setup an SQLite database to hold our data:

/src/models/dbconfig.js

 const path = require('path');
 const Sequelize = require('sequelize');
 
 const dbconfig = new Sequelize({
 	dialect: 'sqlite',
 	storage: path.join(__dirname, 'store.db')
 });
 
 module.exports = dbconfig;

Then we define our entities:

/src/models/Item.js

 
const Sequelize = require('sequelize');
const dbconfig = require('./dbconfig');
 
class Item extends Sequelize.Model { }
 
Item.init({
 	id: {
 		type: Sequelize.INTEGER,
 		autoIncrement: true,
 		primaryKey: true,
 	},
 	name: Sequelize.STRING,
 	description: Sequelize.STRING,
 	category: Sequelize.STRING,
 	damage: Sequelize.INTEGER,
 	
}, { sequelize: dbconfig });
 
module.exports = Item;

/src/models/Player.js

 const Sequelize = require('sequelize');
 const dbconfig = require('./dbconfig');
 const Item = require('./Item');
 
 class Player extends Sequelize.Model {}
 
 Player.init({
 	id: {
 		type: Sequelize.INTEGER,
 		autoIncrement: true,
 		primaryKey: true, 		
 	}, 	
 	name: Sequelize.STRING,
 	class: Sequelize.STRING,
 	level: Sequelize.INTEGER,
 	score: Sequelize.INTEGER,
 }, { sequelize: dbconfig });
 
 Player.hasMany(Item);
 
 module.exports = Customer;
 

This is the standard way to write your sequelize models which handles your basic needs within your app. A couple things to note however:

  1. The database configuration is hardwired into the models.

  2. It is awfully difficult to use different databases to run your app using this approach as is usually the case in a test-driven development environment. You usually want to run your tests on a different database so you don’t garble your data (or worse, your customer’s data).

Fixing the problem

The idea is to write a factory method that recieves the database configuration as a parameter when it creates the models. Using this approach, we can plug the database models into any database we want to run our code on.

Let’s modify our models:

/src/models/Item.js

const Sequelize = require('sequelize');
const dbconfig = require('./dbconfig');

class Item extends Sequelize.Model {}

const createItemModel = (dbconfig) => {
	let model = Item.init({
		id: {
			type: Sequelize.INTEGER,
			primaryKey: true,
			autoIncrement: true,
		},
		name: Sequelize.STRING,
	 	description: Sequelize.STRING,
 		category: Sequelize.STRING,
 		damage: Sequelize.INTEGER,
	}, { sequelize: dbconfig });
	
	return model;
};

module.exports = createItemModel;

/src/models/Player.js

const Sequelize = require('sequelize');
const dbconfig = require('./dbconfig');
const createItemModel = require('./Item');

class  Player extends Sequelize.Model {}

const createPlayerModel = (dbconfig) => {
	let model = Player.init({
		id: {
 			type: Sequelize.INTEGER,
 			autoIncrement: true,
 			primaryKey: true, 		
 		}, 	
 		name: Sequelize.STRING,
 		class: Sequelize.STRING,
 		level: Sequelize.INTEGER,
 		score: Sequelize.INTEGER,		
	}, { sequelize: dbconfig });

	model.hasMany(createItemModel(dbconfig));
	
	return model;
};

module.exports = createPlayerModel;

By doing this, we can inject the database configuration into the models and change it anytime we want.

Testing out our new models

To use our fancy new models we just have to setup our database configuration and inject this config into our new model factory methods like so:

/src/modelTest.js

const dbconfig = require('./models/dbconfig');
const createItemModel = require('./models/Item');
const createPlayerModel = require('./models/Player');

let Player = createPlayerModel(dbconfig);
let Item = createItemModel(dbconfig);

const setupGame = () => {

	return Item.create({
		name: 'Thor hammer',
		description: 'The hammer from that cheesy movie',
		category: 'weapon',
		damage: 50
	}).then((hammer) => {
		return Player.create({
			name: 'Thor',
			class: 'warrior',
			level: 10,
			score: 0
		}).then( (thor) => {
			thor.addItem(hammer);
		});
	
	}).then((thorWithHammer) => {
		console.log('Thor got his hammer! ', thorWithHammer);
	});

};

setupGame();

Thanks for reading. Now go and create some crazy new warriors!!!

Published 29 Mar 2019

A software developer that's passionate about Javascript, Python and Java. When I'm not coding, I love to listen to hip hop music and read science fiction books.
Kuro Souza on Twitter