unit-testing-with-the-bl-cli

Unit Testing with the BL CLI

This tutorial shows how to unit test your Business Logic (BL) created with the BL CLI.

This tutorial assumes you have the BL CLI v1.0.0 or higher installed. BL CLI v1.0.0 changed the name of the executable from kinvey to kinvey-bl, and the name of the configuration file from .kinvey to .kinvey-bl. If you are using an earlier version, please remember to use the older filenames where needed.

Set-Up

Typically, when using the BL CLI, your local project has a business-logic/ directory which contains all of your collection hooks, custom endpoints, and common code files. In addition, when generating new BL scripts, the BL CLI will also generate a corresponding test file in the test/ directory.

For new projects, test files will automatically be created when running the init command. For existing projects, if you don’t have the corresponding test files yet, you can generate them using the refresh command:

$ kinvey-bl refresh

Generate test files for your BL

Node.js

To unit test your Business Logic, your project needs to be set-up as a Node.js project. This can be done by creating a package.json file in the root of your project:

{
  "name"    : "<name-of-your-project>",
  "version" : "<version-of-your-project>",
  "scripts": {
    "test": "mocha test/ --recursive"
  },
  "devDependencies": {
    "kinvey-business-logic-testing-library": "0.1.4",
    "mocha": "2.3.4"
  }
}

If you already have a package.json file, all you need to do is add the kinvey-business-logic-testing-library and mocha lines to your projects’ devDependencies. If you'd like to run the test using the shorthand npm test, you should also add the scripts object (along with the test property it contains) as seen in the code snippet above.

At this point, assuming you have Node.js installed, and you have run npm install, you should be able to run the tests:

$ npm test

Running the tests for the first time

Docker

As you can see, the first run of the tests will result in an error like "Failed to reach Docker. Are you sure Docker is running properly?". Since the tests require Docker to run, a Docker machine needs to be set-up and started first. It is not necessary to download any Docker images, the test scripts will do that for you.

Once a Docker machine is running, re-run the tests:

$ npm test

Running the tests

The first time you run the tests, the test script will download two Docker images. Therefore, depending on your network connection, the tests make take multiple minutes to complete. This does not affect subsequent test runs, which should complete within seconds.

Usage

Now that you are able to run the tests, it is time to look at how to actually write tests for your Business Logic. Before doing this, make sure you have proficient familiarity with Mocha, which is the test suite used to run the tests.

Also, make sure to read the Kinvey Business Logic Testing Library documentation, as it will provide you insight of what methods to use in your tests. For now, let’s walk through the test code step by step.

// Standard lib.
var path = require('path');

// Package modules.
var tester = require('kinvey-business-logic-testing-library');

// Configure.
var options = {
  blRootPath    : path.join(__dirname, '../../../business-logic'),
  environmentID : 'kid_xxxxxxxxx'
};

// Test suite.
describe('hello-world', function() {
  // Set-up the client.
  before('client', function(cb) {
    this.timeout(0); // Disable timeout.
    var self = this;
    tester.util.setup(options, function(err, client) {
      self.client = client; // Save.
      cb(err); // Continue.
    });
  });

  // Set the endpoint under test.
  before('configure', function() {
    this.endpoint = 'hello-world';
  });
  after('configure', function() {
    delete this.endpoint; // Cleanup.
  });

  // Teardown the client.
  after('client', function(cb) {
    delete this.client; // Cleanup.
    tester.util.teardown(options, cb);
  });

  // Tests.
  it('should run.', function(done) {
    // Configure the request.
    var requestObject = {
      // Request details here.
    };

    // Run the endpoint.
    this.client.runCustomEndpoint(this.endpoint, requestObject, { }, function(err, blResult) {

      //
      // Assertions here.
      //

      done(err); // Continue.
    });
  });
});

Line-by-line

  • Lines 1–13: Import some packages, like the BL testing library, and configure your project. You’ll notice your projects’ kid, which is automatically inserted when generating test files.
  • Line 14: Start the test suite for the custom endpoint under test.
  • Lines 15–24: Set-up the BL tester. This piece of code ensures the Docker images are up and running, and the client is ready to be used.
  • Lines 25–32: Set this.endpoint to the custom endpoint under test for easy referencing.
  • Lines 33–38: Shut-down the test suite when all tests are done.
  • Lines 39–55: An example test that checks whether the endpoint runs. Typically, when writing tests, you’d have multiple tests each in a separate it function. In this example, the custom endpoint referenced by this.endpoint is ran by the client, which asynchronously returns an error (if any), and the endpoint response.

Example Test

A response returned by runCustomEndpoint or runCollectionHook typically has the following format:

{
  metadata: {
    hookType: 'customEndpoint',
    collectionName: 'hello-world',
    targetFunction: 'onRequest',
    taskId: 'xxx-xxx-xxx-xxx'
  },
  request: {
    method: 'POST',
    headers: { 'X-Custom-Header': 'FooBar' },
    body: {},
    params: {},
    username: 'testUsername',
    collectionName: 'hello-world',
    appKey: 'kid_xxxxxxxxx',
    tempObjectStore: { }
  },
  response: {
    status: 0,
    headers: { },
    body: { hello: 'world' },
    statusCode: 200
  },
  endOnComplete: false,
  rusage: {
    utime: 0,
    stime: 0,
    maxrss: 0,
    ixrss: 0,
    idrss: 0,
    isrss: 0,
    minflt: 0,
    majflt: 0,
    nswap: 0,
    inblock: 0,
    oublock: 0,
    msgsnd: 0,
    msgrcv: 0,
    nsignals: 0,
    nvcsw: 0,
    nivcsw: 0,
    wtime: 0
  },
  taskId: 'xxx-xxx-xxx-xxx'
}

Probably the most interesting part of this response is the response property, which will contain the HTTP status code, response headers, and response body. For example, to test that the custom endpoint completed with status code 200, you’d do something like:

it('should return 200.', function(done) {
  this.client.runCustomEndpoint(this.endpoint, { }, { }, function(err, blResult) {
    console.assert(200 === blResult.response.statusCode); // Test if HTTP status code is 200.
    done(err); // Continue.
  });
});

We recommend using an assertion library, like Chai or Should.js, instead of using console.assert.

Going Further

The example tests discussed in the last section only touch a small portion of the functionality that comes with the BL Testing Library. For example, you can write custom request or response objects, or use mock data to see if your BL script manipulates the right data.

In addition, since the tests are run with one simple command (mocha test/ --recursive), it is very easy to incorporate (automated) testing into your development cycle. Simply run the above command before deploying your BL to make sure it works as intended.

Got a question?