bredikhin / sailsjs-mocha-testing-barrels-fixtures-example

An example of Sails.js-project testing with Mocha and Barrels for fixtures
121 stars 22 forks source link

How to break this up into discreet test suites? #1

Closed davesag closed 10 years ago

davesag commented 10 years ago

Using your example I've got my app's controller tests working for one controller but now I want to break this out to test my other controllers.

How should I best do this so that I only list Sails once and load the fixtures once for the whole sequence of tests, with each test suite split out into a separate file?

Cheers

Dave

bredikhin commented 10 years ago

You can just place your tests in different files (one file per controller) while putting global before / after hooks only once in any of those files (or your can create testHelper.js and put your global hooks there). All the tests from all the files passed to mocha will run in between those two hooks.

davesag commented 10 years ago

Cool thanks. I'm not sure if this is 100% the correct way to approach it but it worked for me.

I created a file testHelper.js as follows:

var Sails = require('sails');

global.assert = require('assert');
global.barrels = require('barrels');
global.sinon = require('sinon'), // Mocking/stubbing/spying
global.chai = require('chai'),   // see http://chaijs.com/guide/styles/
global.expect = chai.expect,     // Expectations lib
global.fixtures = null;
global.mockRequest = {};
global.mockResponse = {
  ajaxRequired: sinon.spy(),
  badRequest: sinon.spy(),
  dbError: sinon.spy(),
  forbidden: sinon.spy(),
  notFound: sinon.spy(),
  ok: sinon.spy(),
  serverError: sinon.spy(),
  unauthorised: sinon.spy(),
  json: sinon.spy()
};
global.responseKeys = Object.keys(mockResponse);

// Global before hook
before(function (done) {
  // Lift Sails with test database
  console.log('Before all Location tests - lifting sails');
  Sails.lift({
    log: {
      level: 'error'
    },
    connections: {
      default: 'test'
    }
  }, function(err, sails) {
    if (err) return done(err);
    // Load fixtures
    Location.destroy().exec(function(err, locs){
      if (err) return done(err);
      User.destroy().exec(function(err, users){
        if (err) return done(err);
        barrels.populate(function(err) {
          fixtures = barrels.objects;
          done(err, sails);
        });
      })
    });
  });
});

// Global after hook
after(function (done) {
  console.log('After test - lowering sails');
  sails.lower(done);
});

and then in my tests I do:

require("../testHelper");

var UserController = require('../../api/controllers/UserController');

// Here goes a module test
describe('User Controller', function() {

  beforeEach(function(){
    mockRequest = {
      session: {},
      body: {},
      wantsJSON: true,
      flash: {}
    };
  });

  afterEach(function(){
    for (var i = 0; i < responseKeys.length; i++ ) {
      // console.log("resetting response." + responseKeys[i]);
      mockResponse[responseKeys[i]].reset();
    }
  });

  describe('validation', function() {

    var goodBody = {
          email: 'test-simple@test.tes'
        },
        badBody = {
          email: 'nosuch-test@test.tes'
        };

    it('Can see the Users in the db', function(){
      User.find(function(err, users){
        var gotUsers = (fixtures.user.length > 0),
              usersAreInTheDb = (users.length === fixtures.user.length);
        assert(gotUsers, 'Expected fixture users');
        assert(usersAreInTheDb, "Expected to find the users in the database");
      });
    });

    it('returns true if requesting validation of the existence of an unknown email', function(done){
      mockRequest.body = badBody;
      UserController.validateEmailDoesNotExist(mockRequest, mockResponse);
      setTimeout(function(){
        expect(mockResponse.json.lastCall.args[0]).to.equal(true);
        done();
      }, 95);
    });

    // etc

  });
});

And this works perfectly.

I read somewhere that I should not need to require("../testHelper") and that mocha will load that for me, but I could not get that to work.

Also I have not been able to work out a way to pass a callback to, or return a Promise from my controller methods, hence the use of setTimeout. I am sure there's a better way however as this way is quite brittle.

Anyway - thanks for your help

Dave