Hapi plugins are a great way to break your web server up into isolated pieces of logic. When you structure your app with plugins you'll get easier code reusability, more maintainability and you'll become a happier, prettier person with better posture and beautiful complexion.

However, there's one thing you should not do. And I see it done disturbingly often (not just in Hapi but in Express too): You should not start your server in the unit tests.

Bad, Bad Example

Consider this code for, uh, Avatar server that, um, http-bends the incoming requests. Whatever that means. We're using the node-config module (if you're don't know about it, go check it out!).

// config/default.js
module.exports = {
  server: {
    port: process.env.PORT || 8000,
    manifest: {
      plugins: {
        good: {
          // Logging & monitoring.
          opsInterval: 100000,
          reporters: [{
            reporter: require('good-console'),
            args: [{
              ops: '*', request: '*', log: '*',
              response: '*', error: '*'
            }]
          }]
        },
        'hapi-celery', {
          brokerUrl: 'amqp://rabbitmq:5672',
          resultBackendUrl: 'redis://localhost:6379/0'
        },
        './plugins/sokkaPlugin': null,
        './plugins/aangPlugin': null
      }
    }
  }
}

And this is our server.js (written in ES6 because ES6 is just so much better than non-ES6).

const Glue = require('glue');
const config = require('config');
const { coroutine: co, fromNode } = require('bluebird');

exports.start = co(function* () {
  let serverManifest = config.get('server.manifest');
  let server = yield fromNode(
    Glue.compose.bind(Glue, serverManifest, serverOptions));
  yield fromNode(server.start.bind(server));

  return server;
}

And we're writing a unit test for aangPlugin. We want to know if the gang can safely get past the Fire Nation defenses to confront the Fire Lord.

It would be just so easy to do something like this, right?

const Server = require('../../server');

beforeEach(function() {
  this.server = server.start()
});

describe('when I\'m flying on my bison', function() {
  it('should dodge the incoming fireballs', function() {
    //this.server.inject(...)
  });
});

Only that this is no good.

You'll probably remember your mum telling you about tightly coupling module dependencies - it's bad! Plus, this way you'll always include all of your plugins. Which might work fine...until you add, for example, hapi-celery which, one can assume, will want to connect to Celery, so suddenly you need to run RabbitMq and Redis when you want to run the tests. Ugh. Not fun.

Good Example

So what to do? Simple - don't start your server in tests. Create a brand new server just for the purposes of testing!

const Hapi = require('hapi');

beforeEach(function() {
  let server = this.server = new Hapi.Server();
  server.connection({port: 8000});

  server.register([
    {register: require('fire-bending-plugin-required-by-aang')},
    {register: require('water-bending-plugin-required-by-aang')}
    {register: require('../aangPlugin')},
  ]);
});

describe('when I\'m flying on my bison', function() {
  it('should dodge the incoming fireballs', function() {
    //this.server.inject(...)
  });
});

In other words - explicitly register all your dependencies in the test fixture and don't call anything like server.start. You have inject to ping your routes even when your server is not bound to a port.

A bit longer but so much more painless in the long-run. Hapi testing!

Questions? Criticism? I'm @tomas_brambora.