hapi — Getting Started With Testing Using Lab and Code

Hapi is our favorite Node.js web framework. One of the reasons we love hapi is the easiness of testing. The built-in server.inject method helps you to follow a test-driven development approach.

This tutorial gets you started with testing in hapi. You’ll set up your project to run tests with npm test and receive all wordings and concepts in testing with lab and code from hapi’s ecosystem.

Once you’re on board with testing, you won’t “just skip tests for this feature, it’ll work fine” 😉

hapi Series Overview

Install Dependencies: lab and code

Lab is a Node.js testing framework within the hapi ecosystem. We’ll use lab for testing and within the code samples to illustrate testing in hapi. You can replace lab with any other library, like AVA, tape, or mocha.

Additionally to lab as a test framework, we’ll use the assertion library code. Again, you can replace code with libraries like chai.

Install lab and code and save the packages as devDependencies:

# Running Node.js v8+
npm i -D lab code

# Running Node.js v6-
npm i -D lab@14 code@4  

Notice that the latest releases of lab and code are only compatible with Node.js v8+. If you’re on Node.js v4 or v6, use lab in version 14 and code in version 4.

Set Your Project Up for Testing

When following the traditional TTD, you’ll write your tests first. Ensure a solid feature implementation by defining and creating the tests first.

You can surely add tests to an existing project, no need to start from the green field.

We’ll use the Futureflix Starter Kit in this tutorial to illustrate the setup of tests. You’ll go ahead with your own project 😃

Create a Dedicated test Directory

The first task is to create a test directory in your project’s root.

Let’s say your project files are in a directory called futureflix-starter-kit. Add a test folder in your project’s root directory. If the test folder already exists, go ahead and use it. You don’t need to create a folder like test-new, keep the existing one.

futureflix-starter-kit  
|_ public
|_ test             <-- add this folder
|_ …
|_ web
|_ package.json
|_ server.js

You’ll place all your test files within the test directory. Read the next section to prepare the npm test command in your package.json file to use lab for testing.

Update package.json To Run Your Tests with Lab

NPM allows you to define commands. Run them with the npm command line utility. Common commands are npm install, npm start and also npm test.

Make use of these command NPM shortcuts. Open your project’s package.json file and search for the script tag. Look out for the test key and replace the default echo \"Error: no test specified\" && exit 1 value by lab --assert code --leaks --coverage.

{
  "name": "futureflix",
   …
  "scripts": {
    "test": "lab --assert code --coverage"
  }
}

Now, when running npm test, NPM evaluates and runs the test command from the scripts block in your package.json file. NPM aliases the lab command from node_modules so you don’t need to define node_modules/lab/bin/lab.js --assert ….

The flags --assert and --coverage tell lab to enable these options while running the tests.

With --assert code you’re defining code as the library for assertions. You won’t read further details on code in this tutorial, but you should install and add it within the test command already. The next tutorial goes all in on assertions with code. Hope you stay hungry 😉

The flag for --leaks enables memory leak detection in lab and warns you if it detects one. The --coverage flag prints the code coverage besides the test results. You don’t necessarily need to cover 100% of your code with tests. 100% code coverage doesn’t mean your project is free of bugs and your tests cover every use case. Write tests to cover the important parts of your app and the coverage outline will help you with that.

Run npm test Again to Verify the Setup

Run npm test to verify your setup. Lab loads and runs every file located within the test folder. At this point, you don’t have any tests. You command line output should look like this:

$ npm test

> futureflix@1.1.0 test /Users/marcuspoehls/Dev/FutureStudio/futureflix-starter-kit
> lab --assert code --leaks --coverage

The pattern provided (-P or --pattern) didn't match any files.  

Nothing to do for lab. Time to create your first test!

Create your First Test

The test directory is in place and the NPM script to run tests makes use of lab. There’s nothing to do yet for lab.

Because we’re using the Futureflix Starter Kit to illustrate the test setup, let’s put all test files in a dedicated folder inside the test directory. Call the dedicated folder getting-started.

Now, create a new file in the test/getting-started directory. Name it 01-getting-started.js. Well, the file name is creative. To spoiler, there will be a test in this file that always succeeds. You’ll get to know the first part of lab’s building blocks 😁

Within your building-blocks.js test file, import and assign variables for lab and code.

01-getting-started.js

'use strict'

const Lab = require('lab')

// Test files must require the lab module, and export a test script
const lab = (exports.lab = Lab.script())

// shortcuts to functions from lab
const experiment = lab.experiment  
const test = lab.test

experiment('getting started with hapi testing,', () => {  
  test('TODO')
})

Notice the exports.lab = Lab.script(). It’s a requirement to export a lab script when running tests with lab.

Besides lab’s export, you can see the creation of two shortcuts: experiment and test. Read more about them in the next paragraph.

Group Tests Into Experiments

If you want to group tests by category or topic, use lab’s experiment.

experiment('test the sign up,', () => {  
  test('sign up fails without email address')
  test('sign up fails without password')
})

experiment('test the login,', () => {  
  test('login fails without email address')
  test('login fails for not registered email address')
})

Let’s illustrate a use case of experiments to get a grasp on it. Assume you’re writing a hapi plugin that handles user signup and log in. The sign-up and login are separate features. You can use a single test file and test both features in there.

Separate the tests for the sign up within a dedicated experiment from the login tests. You can also create different test files to isolate the testing for sign up and log in. Experiments are a good way to structure your tests by features.

Assign each experiment a title, like test the sign-up, (the comma at the end is no typo). Lab concatenates this title with the test’s title and show it for failing tests. We use a comma at the end of the experiment’s title as a separator between the two titles.

Create a Test

Finally, you can use the test shortcut and create an actual test. The test shortcut is a function that takes three optional arguments:

The title as the first parameter. The second parameter is an options object. More about the options in the next section. The third parameter is a callback(done) function providing another done(err) callback itself.

If you don’t provide the callback function when calling test(), lab considers it as a TODO and skips this test. The test above is one that lab skips:

experiment('getting started with hapi testing,', () => {  
  test('lab considers this as a TODO')
})

To complete a test successfully, call done(), without anything, no error, no data, nothing. Passing data or an error to the done callback makes the test fail.

Let’s create a second test that lab won’t skip. This test will always succeed because you’re calling done() right away:

experiment('getting started with hapi testing,', () => {  
  test('lab considers this test as TOOD and skips it')

  test('always succeeding :)', () => {})
})

The second test has the done callback and that means you need to call it to tell lab the test fails or succeeds. If you don’t call done(err), it’ll time out after lab’s timeout interval. The default timeout for a test in lab is 2,000ms.

Options for Tests and Experiments

Both, the test and experiment functions accept the same options. You can pass an options object as the second argument to both functions.

The allowed options are:

  • timeout: set a specific timeout in milliseconds. Defaults to 2,000 ms or the value of -m
  • parallel: activates parallel execution of tests within each experiment level. Defaults to false (serial execution)
  • skip: skip execution. Cannot be overridden in children once you set up the parent to skip
  • only: marks all other tests or experiments with skip

Provide an options object to the experiment that runs every test in parallel:

experiment('getting started with hapi testing,', { parallel: true }, () => {  
  test('lab considers this test as TODO and skips it')

  test('always succeeding :)', { timeout: 5000 }, () => {})
})

Another way to set the only and skip options is to chain methods with the same name, like this:

experiment('getting started with hapi testing,', { parallel: true }, () => {  
  test.skip('lab considers this test as TODO and skips it')

  test('always succeeding :)', { timeout: 5000 }, () => {})
})

You can’t use .parallel() or .timeout() on tests and experiments. To define these options, use the options object.

Run Your Tests: npm test

The last time you ran npm test, there was no test file to execute. With the newly created test, run it again and verify whether your code works properly.

If everything is fine, you should see an output like this:

$ npm test

> futureflix@1.1.0 test /Users/marcuspoehls/Dev/FutureStudio/futureflix-starter-kit
> lab --assert code --leaks --coverage



  -.

1 tests complete (1 skipped)  
Test duration: 8 ms  
Assertions count: 0 (verbosity: 0.00)  
Coverage: 0.00% (0/0)  

The 01-getting-started.js test file contains a single experiment with two tests. Lab skips the first test because you didn’t define a done(err) callback. The second test succeeds.

You made it through the initial setup. You know that this is the beginning of testing your hapi application. For today, you made great progress! 🤘

Outlook

Your hapi project is ready for test-driven development 😉 The hapi ecosystem provides a Node.js test framework called lab. We trust and like lab, it’s a great tool.

If you want to use another testing tool, replace lab with your favorite one.

Testing with hapi is enjoyable. The next tutorial on testing in hapi shows you how to use code to assert results.

We’re happy to hear your thoughts and appreciate your comment. Please don’t hesitate to use the comment form below. If Twitter is more your thing, find us @futurestud_io. Let us know what you think!

Enjoy coding & make it rock!


Mentioned Resources

Explore the Library

Find interesting tutorials and solutions for your problems.