2

I'm trying to set up a testing pattern for a new Hapi app. I've used Mocha and Chai in the past with Express, but I'm trying to use Lab and Code to stay in the Hapi ecosystem. I'm also using Bookshelf and Knex to handle database interaction.

So I have a simple health endpoint I'd like to test.

'use strict';

const controller = require('../controller/healthController');

module.exports = [
  {
    method: 'GET',
    path: '/health',
    config: {
      handler: controller.health,
      description: 'The health endpoint returns 200',
      tags: ['api', 'health']
    }
  }
];

In the handler it just does a quick query to make sure it can connect to the DB.

'use strict';

const bookshelf = require('../config/db');
const knex = bookshelf.knex;

module.exports = {
  health: function (request, reply) {
    knex.raw('SELECT version()').then(() => {
      reply('service is running');
    }).catch((err) => {
      reply({err: err, code: 500});
    });
  }
};

As far as I understand it, requiring the server and then using server.inject doesn't actually start the server so I don't believe I should have a db connection, which would mean I should have to mock it out the db call. What is odd to me is that this test passes:

'use strict';
const Code = require('code');
const Lab = require('lab');

const lab = exports.lab = Lab.script();
const describe = lab.describe;
const it = lab.test;
const expect = Code.expect;
const before = lab.before;

let server;

describe('health controller', () => {

  before((done) => {
    server = require('../../server');
    done();
  });

  it('health check replies 200 when successful call to db', (done) => {

    const options = {
      method: 'GET',
      url: '/health'
    };

    server.inject(options, (res) => {
      expect(res.payload).to.include('is running');
      expect(res.statusCode).to.equal(200);

      done();
    });

  });

});

So I have two problems. First, I feel like the test above shouldn't really pass. Unless it's loading everything up and thus connecting to the db I suppose. Maybe I should be testing just the controller/handler method? But I haven't found any examples of that.

Second, I've tried to stub out the knex.raw call anyway and when I try to do it like below I get a 500 error.

'use strict';
const Code = require('code');
const Lab = require('lab');
const Sinon = require('sinon');

const lab = exports.lab = Lab.script();
const describe = lab.describe;
const it = lab.test;
const expect = Code.expect;
const before = lab.before;

let server;
let knex = require('../../app/config/db').knex;

describe('health controller', () => {

  before((done) => {
    server = require('../../server');
    done();
  });

  it('health check replies 200 when successful call to db', (done) => {

    const stub = Sinon.stub(knex, 'raw').returns({});

    const options = {
      method: 'GET',
      url: '/health'
    };

    server.inject(options, (res) => {
      expect(res.payload).to.include('is running');
      expect(res.statusCode).to.equal(200);
      expect(stub.calledOnce).to.be.true();
      done();
    });

  });

});

I'm not really sure why that's happening.

rschlachter
  • 720
  • 1
  • 9
  • 20
  • How does your `require('../../app/config/db')` looks like? In first stage try to inline all the query code in your test case file to be able to make sure that mocking works as you expect and then piece by piece move code to correct place to find exact place where stuff didn't work as you expected. – Mikael Lepistö Jan 19 '17 at 09:35
  • Please include source code of the `'../../server'`. Please include message of the 500 error and any logs from your console. – Michał Miszczyszyn Jan 29 '17 at 21:01
  • Was my answer helpful to you in any way? – Michał Miszczyszyn Mar 28 '17 at 23:22

2 Answers2

3

I think Sinon.stub(knex, 'raw').resolves({}); is a better solution

Bobby J
  • 31
  • 2
1

server.inject works exactly as if you made a request to a real server. So if your database is up when the test are run, the endpoint will return data from the database just like it does when you start the server manually.

Sinon.stub(knex, 'raw').returns({}); won't work. knex.raw(…) is expected to return a Promise, not an empty object. Please try the following:

Sinon.stub(knex, 'raw').returns(Promise.resolve({}));

Just a side note: Remember to call server.stop() after each test in order to ensure there's no state persisted between tests. In general, I think you can take a look at example test files in Hapi University repository.

Michał Miszczyszyn
  • 11,835
  • 2
  • 35
  • 53