Mocha, expect.js e Sequelize: Uma introdução

Introdução ao Sequelize com testes automatizados usando Mocha e expect.js

#dev, #javascript

Nota: esse post é bastante semelhante ao anterior Jasmine e Sequelize: Uma Introdução.

Criar o projeto

Vamos usar o npm para criar o projeto:

$ npm init

Siga as instruções. Após executar o comando será criado um arquivo package.json semelhante a esse:

{
  "name": "mocha-sequelize.js",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "Dmitry Rocha <blog@dmitryrck.com>",
  "license": "MIT"
}

Agora colocaremos os arquivo de dependência do projeto:

$ npm install --save sequelize pg pg-hstore
$ npm install --save-dev mocha expect.js

Continuo sem saber o porquê dele precisar do hstore.

Lembrando que o --save e o --save-dev coloca os pacotes como dependências no package.json, respectivamente, para o ambiente padrão e para o ambiente de desenvolvimento.

Nosso package.json deve ter ficado semelhante este:

{
  "name": "mocha-sequelize.js",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "Dmitry Rocha <blog@dmitryrck.com>",
  "license": "MIT",
  "dependencies": {
    "pg": "^4.3.0",
    "pg-hstore": "^2.3.2",
    "sequelize": "^3.1.0"
  },
  "devDependencies": {
    "expect.js": "^0.3.1",
    "mocha": "^2.2.5"
  }
}

Como você pode ter notado o package.json tem uma linha que descreve os scripts do projeto e como estamos escrevendo testes vamos adicionar como executá-los.

"scripts": {
  "test": "mocha test/**test.js"
},

Um fato interessante do mocha é que é necessário dizer exatamente qual o arquivo do teste, caso contrário ele roda, mas não executa nenhum dos aquivos.

O Ambiente de Teste

Vamos criar o primeiro teste test/unit/task.test.js:

process.env.NODE_ENV = "test";

describe("Task", function() {
  var Task = require("../../models").task;

  beforeEach(function() {
    Task.sync();
  });

  afterEach(function() {
    Task.destroy({ truncate: true });
  });

  it("create", function() {
    Task.create({ title: 'a title' }).then(function(task) {
      expect(task.title).toBe('a title');
    });
  });
});

Como certo a se fazer vamos rodar o teste:

$ mocha test/unit/task.test.js
module.js:338
    throw err;
          ^
Error: Cannot find module '../../models'
    at Function.Module._resolveFilename (module.js:336:15)
    at Function.Module._load (module.js:278:25)
    at Module.require (module.js:365:17)

[ … ]

Pois bem, não encontrou o ../../models, criá-lo-emos ;)

Citando meu post anterior:

Mas primeiro: se você executar um require de um diretório no nodejs, ele procurará por um arquivo index.js.

O nosso models/index.js deve ser algo como (idêntico ao do post de jasmine):

var fs = require("fs");

var DataTypes = require('sequelize');
var sequelize = new DataTypes('mocha_' + process.env.NODE_ENV, 'postgres', '', { dialect: 'postgres' });

var db = {};

fs
  .readdirSync(__dirname)
  .filter(function(file) {
    return(file.indexOf(".") !== 0) && (file !== "index.js");
  })
  .forEach(function(file) {
    var model = sequelize["import"](__dirname + '/' + file);
    db[model.name] = model;
  });

db.DataTypes = DataTypes;
db.sequelize = sequelize;

module.exports = db;

* Lembre-se: models na raiz do projeto.

Esse index.js procura por arquivos no mesmo diretório que ele está, que não seja ele mesmo e nem inicie com . (arquivos ocultos), e carrega esses arquivos com o import do sequelize.

Nosso teste continua quebrando:

$ mocha test/unit/task.test.js
/home/dmitry/Dev/mocha-sequelize.js/test/unit/task.test.js:4
  var Task = require("../../models").task;
                                    ^
TypeError: Cannot read property 'task' of undefined
    at Suite.<anonymous> (/home/dmitry/Dev/mocha-sequelize.js/test/unit/task.test.js:4:40)
    at context.describe.context.context (/home/dmitry/.node_modules/lib/node_modules/mocha/lib/interfaces/bdd.js:
[ … ]

Então… arquivo /models/task.js:

module.exports = function(sequelize, DataTypes) {
  return sequelize.define('task', {
    title: DataTypes.STRING,
    complete: DataTypes.BOOLEAN
  });
};

Crie a base de dados por fora:

$ createdb mocha_test

Pronto nosso teste passará:

$ mocha test/unit/task.test.js

  Task
    ✓ create

  1 passing (70ms)

O código como de costume está no github em: https://github.com/dmitryrck/mocha-sequelize.js.

Referências