Edit Page

Testing your code

This section of the documentation runs through how you might go about testing your Sails application. There are countless test frameworks and assertion libraries for Sails and Node.js; pick one that fits your needs.

There is no official strategy for testing in the Sails framework, and this page is a collaborative, community-driven guide that has not been thoroughly vetted by Sails core team members. If you run across something that seems confusing or incorrect, feel free to submit a pull request.

Preparation

#

For our example test suite, we'll use mocha.

npm install mocha --save-dev

Before you start building your test cases, organize your test/ directory structure. Once again, when it comes to automated testing, there are several different organizational approaches you might choose. For this example, we'll go about it as follows:

./myApp
├── api/
├── assets/
├── ...
├── test/
│  ├── integration/
│  │  ├── controllers/
│  │  │  └── UserController.test.js
│  │  ├── models/
│  │  │  └── User.test.js
│  │  └── helpers/
|  |     └── ...
│  ├── fixtures/
|  │  └── ...
│  ├── lifecycle.test.js
│  └── mocha.opts
├── ...
└── views/

lifecycle.test.js
#

This file is useful when you want to execute some code before and after running your tests (e.g. lifting and lowering your Sails application). Since your models are converted to Waterline collections on lift, it is necessary to lift your Sails app before trying to test them (this applies controllers and other parts of your app, too, so be sure to call this file first).

var sails = require('sails');

// Before running any tests...
before(function(done) {

  // Increase the Mocha timeout so that Sails has enough time to lift, even if you have a bunch of assets.
  this.timeout(5000);

  sails.lift({
    // Your Sails app's configuration files will be loaded automatically,
    // but you can also specify any other special overrides here for testing purposes.

    // For example, we might want to skip the Grunt hook,
    // and disable all logs except errors and warnings:
    hooks: { grunt: false },
    log: { level: 'warn' },

  }, function(err) {
    if (err) { return done(err); }

    // here you can load fixtures, etc.
    // (for example, you might want to create some records in the database)

    return done();
  });
});

// After all tests have finished...
after(function(done) {

  // here you can clear fixtures, etc.
  // (e.g. you might want to destroy the records you created above)

  sails.lower(done);

});
mocha.opts
#

This file is optional. You can use it as an alternative to command-line options for specifying custom Mocha configuration.

One notable customization option is timeout. The default timeout in Mocha is 2 seconds, which is sufficient for most test cases but may be too short depending on how often your tests are lifting and lowering Sails. To ensure that Sails lifts in time to finish your first test, you may need to increase the timeout value in mocha.opts:

--timeout 10000

Note: If you are writing your tests in a transpiled language such as CoffeeScript (.coffee files instead of .js files), you'll need to take an extra step to configure Mocha accordingly. For example, you might add these lines to your mocha.opts:

--require coffee-script/register
--compilers coffee:coffee-script/register

_If you prefer Typescript, the approach is basically the same, except you'll want to use --require ts-node/register.

Writing tests

#

Once you have prepared your directory, you can start writing your integration tests:

// ./test/integration/models/User.test.js

var util = require('util');

describe('User (model)', function() {

  describe('#findBestStudents()', function() {
    it('should return 5 users', function (done) {
      User.findBestStudents()
      .then(function(bestStudents) {

        if (bestStudents.length !== 5) {
          return done(new Error(
            'Should return exactly 5 students -- the students '+
            'from our test fixtures who are considered the "best".  '+
            'But instead, got: '+util.inspect(bestStudents, {depth:null})+''
          ));
        }//-•

        return done();

      })
      .catch(done);
    });
  });

});

Testing actions & controllers

#

The most fundamental tests for your backend code involve sending an HTTP request and checking the response. There are numerous ways to go about this, whether it's a full-fledged testing tool, like Supertest, or a pure utility like request or mp-http, combined with assert.

Using Supertest
#

Let's take Supertest for a spin:

npm install supertest --save-dev

The idea behind Supertest is to provide a high-level tool that helps build a specific type of test—specifically, the type of test that send an HTTP request to your Sails app and checks the response.

// test/integration/controllers/UserController.test.js
var supertest = require('supertest');

describe('UserController.login', function() {

  describe('#login()', function() {
    it('should redirect to /my/page', function (done) {
      supertest(sails.hooks.http.app)
      .post('/users/login')
      .send({ name: 'test', password: 'test' })
      .expect(302)
      .expect('location','/my/page', done);
    });
  });

});

Running tests

#

In order to run your test using Mocha, you'll have to use mocha in the command line then pass as arguments any test you want to run. Be sure to call lifecycle.test.js before the rest of your tests, like this: mocha test/lifecycle.test.js test/integration/**/*.test.js

Using npm test to run your test
#

You can modify your package.json file to use npm test instead of Mocha, and thus avoid typing out the Mocha command described above. This is particularly useful when calling lifecycle.test.js.

On the scripts dictionary, add a test key and use the following as its value: mocha test/lifecycle.test.js test/integration/**/*.test.js. This looks like:

"scripts": {
    "start": "node app.js",
    "debug": "node debug app.js",
    "test": "node ./node_modules/mocha/bin/mocha test/lifecycle.test.js test/integration/**/*.test.js"
  }

The * is a wildcard used to match any file inside the integration/ folder that ends in .test.js. If it suits you, you can modify it to search for *.spec.js instead. In the same way, you can use wildcards for your folders by using two * instead of one.

As of Sails v1, Sails apps are generated with a test script already in their package.json file, but you'll still want to make some modifications to it for this example. If you're upgrading an existing app, you may have to add a test key by hand.

Continuous integration

#

If you'd like to have a system automatically run your tests every time you push to your source code repository, you're in luck! Many different continuous integration systems support Sails/Node.js, so you can have your pick. Here are a few popular choices to get you started:

All of the options above charge a monthly fee for proprietary apps but are free for open source. Circle CI is free for proprietary apps as well, but throttled to two builds at a time. Semaphore is also free and and allows you 4x parallel CI/CD jobs.

Load testing

#

A number of commercial options exist for load testing web applications. You can also get a reasonable idea of how your app will perform using tools like ab or JMeter. Just remember, the goal is to simulate real traffic. For more help setting up your Sails app to be production-ready and scalable, see Scalability. For additional help or more specific questions, click here.

Optimizing performance

#

Usually, the scalability and overall performance of your app is more important than the performance and latency of any given individual request to a particular endpoint. So rather than focusing on one piece of code in isolation, we recommend starting with the basics; for most apps, that's good enough. For some use cases (e.g. serving ads, or apps with very computationally-intensive functionality), though, individual request latency may be important from the get-go.

For testing the performance of particular chunks of code, or for benchmarking the latency of individual requests to particular endpoints, a great option is benchmark.js. Not only is it a robust library that supports high-resolution timers and returns statistically significant results, it also works great with Mocha out of the box.

Is something missing?

If you notice something we've missed or could be improved on, please follow this link and submit a pull request to the sails repo. Once we merge it, the changes will be reflected on the website the next time it is deployed.

Concepts