Testing REST backend services

It's now time to turn our attention to the user authentication service. We've mentioned tests of this service, saying that we'll get to them later. "Later" is now, it seems. While we can test this service as we did for the Notes models, which would be to just call the methods and check the results, we have an opportunity to test its REST API instead. The customer of this service, the Notes application, uses it through the REST API, giving us a perfect rationalization to test using REST.

The approach we'll take is to use Mocha and Chai as we did earlier using the restify client to make REST calls inside test cases.

We've already made the test-compose/userauth directory. In that directory, create a file named test.js:

'use strict';

const assert  = require('chai').assert;
const restify = require('restify');
const url     = require('url');

var usersClient;

describe("Users Test", function() {
    before(function() {
        usersClient = restify.createJsonClient({
          url: url.format({
            protocol: 'http',
            hostname: process.env.HOST_USERS_TEST,
            port: process.env.PORT
          }),
          version: '*'
        });
        usersClient.basicAuth('them',
                   'D4ED43C0-8BD6-4FE2-B358-7C0E230D11EF');
    });
 ..
});

This sets up Mocha and the Restify client. The HOST_USERS_TEST environment variable specifies the hostname to run the test against. This variable was already set in test-compose/docker-compose.yml to "localhost". The before hook, used to set up the REST client, is run once at the beginning of the test suite:

beforeEach(function() {
    return new Promise((resolve, reject) => {
        usersClient.post('/find-or-create', {
            username: "me", password: "w0rd", provider: "local",
            familyName: "Einarrsdottir", givenName: "Ashildr",
            middleName: "", emails: [], photos: []
        },
        (err, req, res, obj) => {
            if (err) reject(err);
            else resolve();
        });
    });
});

afterEach(function() {
    return new Promise((resolve, reject) => {
        usersClient.del('/destroy/me', (err, req, res, obj) => {
            if (err) reject(err);
            else resolve();
        });
    });
});

This uses the API to create our test account before each test case execution, then used again later to delete that account:

describe("List user", function() {
    it("list created users", function() {
        return new Promise((resolve, reject) => {
            usersClient.get('/list', (err, req, res, obj) => {
                if (err) reject(err);
                else if (obj.length <= 0)
                     reject(new Error("no users found"));
                else resolve();
            });
        });
    });
});

Now, we can turn to testing some API methods, such as the /list operation.

We'd already guaranteed that there is an account, in the before method, so /list should give us an array with at least one entry.

This particular testcase can be written using Mocha's support for asynchronous test cases rather than using a Promise object.

describe("List user", function() {
    it("list created users", function(done) {
        usersClient.get('/list', (err, req, res, obj) => {
            if (err) done(err);
            else if (obj.length <= 0)
                 done(new Error("no users found"));
            else done();
        });
    });
});

You should take the approach you prefer. Maybe as you grow comfortable with the Promise object, you'll start applying it everywhere.

This approach uses the traditional Node.js asynchronous coding model. The two are equivalent, other than the lines of code. Perhaps, for this test, this implementation is preferable:

describe("find user", function() {
    it("find created users", function() {
        return new Promise((resolve, reject) => {     
            usersClient.get('/find/me', (err, req, res, obj) => {
                if (err) reject(err);
                else if (!obj)
                     reject(new Error("me should exist"));
                else resolve();
            });
        });
    });
    it("fail to find non-existent users", function() {
        return new Promise((resolve, reject) => {     
            usersClient.get('/find/nonExistentUser', 
            (err, req, res, obj) => {
                if (err) resolve();
                else if (!obj) resolve();
                else reject(new Error("nonExistentUser should not exist"));
            });
        });
    });
});

We can check the /find operation in two ways, looking for the account we know exists, and for the one we know does not exist. In the first case, we indicate failure if the user account is for some reason not found. In the second, we indicate failure if the user account is found:

describe("delete user", function() {
    it("delete nonexistent users", function() {
        return new Promise((resolve, reject) => {
            usersClient.del('/destroy/nonExistentUser', 
            (err, req, res, obj) => {
                if (err) resolve();
                else reject(new Error("Should throw error"));
            });
        });
    });
});

Finally, we should check the /destroy operation. We already check this operation in the after method, where we destroy a known user account. We need to also perform the negative test and verify its behavior against an account we know does not exist.

We have already added configuration to test-compose/docker-compose.yml and the necessary script in users/package.json. Therefore, we go ahead and add these lines to the run.sh file:

docker exec -it userauth-test npm install [email protected] [email protected]
docker exec -it userauth-test npm run test-docker

Then, execute run.sh to rerun the tests, and you'll see this new test run giving its results.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset