Add Customer API – CustomerController –Integration Test – Node.js API with TDD Tutorial

Let’s develop the integration test and functional code of CustomerController’s API endpoint for the Add Customer API in Node.js with TDD.

Customer Controller – Add Customer API Integration Test

We have completed the unit tests for both middleware and service so far. How about testing the controller functions? Since the controller contains the route definitions and handler functions for the API endpoints, the test we could run is called an Integration Test, which mainly tests the end to end functionality of the API endpoint. The integration test can be used to test the module’s single functionality with all the layers underneath grouped together. 

So, testing the controller’s route definition will execute the middleware function calls, and in turn, service function calls till the last layer in the stack of the API endpoint.

Here we cannot mock or stub any of the dependencies for any of the functions in the execution flow. Thus, we will end up creating the actual customer document in the MongoDB collection when we run the integration test for the add customer API endpoint definition.

In this post, we will go over the steps to create the integration test suites for the Add Customer functionality for both successful customer creation and failed customer creation execution paths.

CustomerController Spec File Preparation

Just like the unit test spec file, we will start by setting up the integration test spec file as well. Since we will be testing only the controller file per module, to test all the functionality of that particular module, we may not need to create directory ‘customer‘ under the /tests/integration to create the integration test suite files for the customer module. 

If we add more than one controller file to this module for some reason, later on, we could refactor the directory structure with a module-specific directory for all it’s controller spec files grouped them.

1. Load Required Test Modules

For now, let’s create the customer.controller.spec.js file under the /tests/integration directory with below initial content.

'use strict';

var chai = require('chai');
var chaiHttp = require('chai-http');
chai.use(chaiHttp);

var expect = chai.expect;
var request = chai.request;

These are lines usual test file contents except one new module, ‘chai-http’. As the controller file will contain the routing definitions for the API endpoints to handle all the incoming requests specific to the customer module, we have to test the request and response related functionalities at this file. To test these HTTP request and response, we would need a testing library which supports HTTP testing. 

Chai has several plugins to support various unit and integration testing needs of a developer. One of the integration plugins for HTTP is, chai-http. This Chai assertion library’s plugin supports testing HTTP apps or external services by integration testing with request composition and response assertions.

As usual, this plugin can be installed within the express application with the below command, which will add it as a development dependency for the customer-service application.

npm install chai-http —save-dev

2. Load Fixtures

Once the required modules are loaded for integration testing, we need to load the file under test and fixtures for the testing. This task is accomplished with the below lines.

var app = require('../../app');

var Fixtures = require('../fixtures/fixtures');
var CustomerFixture = Fixtures.CustomerFixture;

Here, we are creating a variable ‘app‘ and loaded with the whole express application itself, by requiring the app.js file from the root directory. So then, loading the customer fixture from the fixtures file.

We need to know the base URI for the Customer API endpoints so that we could form the complete URI for testing and developing the individual API endpoint’s functionalities. Here is the base URI we could start with.

var baseUri = '/customers';

3. Prepare Main Test Suite

Finally, we will create the main test suites for the controller file as below. All the test specs will be added within this main describe() block as we go ahead with creating the integration test cases for all the endpoints of this module.

describe('CustomerController', function () {

});

Integration Test 1: Add Customer API

Let’s begin the integration test suite for creating new customer API with the empty describe block, where we will continue with the spec further.

Test Suite

As you know already, from the sequence diagram of low-level design, to create a new customer, we will use the POST HTTP method for the API endpoint. This HTTP method will be associated with the endpoint URI ‘/customers’, which tells the express application that we want to add a new customer to the system. 

describe("POST " + baseUri, function () {
    
});

Test Script

Inside this test suite, the test spec for calling the add customer API endpoint will be added. Below is the initial code with it() block for this spec.

it('should add new customer', function (done) {
    
});

The parameter ‘done’ in this spec is added to be passed as a callback to the HTTP request so that once the asynchronous request is completed, it will be invoked. This invocation of the callback will indicate that the HTTP call to the API endpoint is completed and then the spec’s execution will be finished. 

This capability is provided by Mocha framework to enable the developers to write unit/integration tests for any asynchronous code, APIs or external systems.

Now, we will write the code to test the behavior of the add customer API. Below is completed test script for this spec. Let’s go through each component of this code block one by one.

describe("POST " + baseUri, function () {
    it('should add new customer', function (done) {

        request(app)
            .post(baseUri)
            .send(CustomerFixture.newCustomer)
            .end(function (err, res) {

                expect(res.status).to.equal(201);
                expect(res.body).to.not.equal({});
              expect(res.body._id).to.not.equal(undefined);
expect(res.body.firstName).to.equal(CustomerFixture.createdCustomer.firstName);

                done();
                
            });

    });
});

In this spec, we are using the chai’s HTTP plugin for composing the request and handling the response object of the API endpoint. The below piece of code represents the request composition and invokes the POST HTTP request to the baseUri, /customers, with the newCustomer fixture content as the request body. 

request(app)
    .post(baseUri)
    .send(CustomerFixture.newCustomer)
    .end(function (err, res) {

        done();        
    });

As this is an asynchronous call, the end() block waits until the request is completed and invokes the anonymous function inside to continue with the testing to verify the expected behavior of the API.

We will see how the expected behavior is confirmed in just a while. Once the verification is completed the callback function done() is invoked to let the Mocha test runner know that the test is finished and it’s execution can be ended at this moment.

Let’s continue with the rest of the piece of code within the end() block now. As the asynchronous request returns either successful data or the error thrown from the API call, we are getting both the error and response objects in this block.  

As this test is an integration test and we cannot mock or stub any dependencies and alter the behavior or the response of the code under test, we can add the expected behavior verification scripts.

If all the behavior is as per our scripted expectation in this code block, we can assume that the API endpoint is working as expected otherwise there is an integration issue, and we need to look into it to fix it.

expect(res.status).to.equal(200);
expect(res.body).to.not.equal({});
expect(res.body._id).to.not.equal(undefined);
expect(res.body.firstName
).to.equal(CustomerFixture.createdCustomer.firstName
);

The first expectation would be that the HTTP status code from the response should be successful with 200 and the rest of the expectations are based on the expected createdCustomer fixture as the response body should be the same as the expected data that we provided. Here we are testing for certain parts of the response object to make sure that it meets the test spec’s expectation. With this script, we can conclude this spec definition.

If we run the test now, we should be getting the error as shown below. We will continue with functional code development to fix the errors one by one in the next section.

Integration Test 1: CustomerController - Add Customer API - Failed 1
Integration Test 1: CustomerController – Add Customer API – Failed 1

Code

It’s time to start adding the required code in the controller file for exposing the API endpoint and adding the API URI’s handling functions. As you know the CustomerController is loading the router object as the module, we will have to add the add customer endpoint URI ‘/customers’ into the router’s post method along with middleware functions added as handler methods for the add customer path URI.

We can begin by creating the variable for loading the CustomerMiddleware module right after the already existing variables for express and router modules assignment in the customer.controller.js file as below.

var CustomerMiddleware = require('./customer.module')().CustomerMiddleware;

Let’s add the below code block after the middleware variable and just before the router object’s assignment to the module.exports object. What we are doing in these statements is, any request to the ‘/customer/’ path with post HTTP method will be handled by this route definition. 

router.post('/',
    function (req, res) {
        res.status(201).json({});
    });

Even though we added the above route definition in the controller file, running the test suites will still yield the same error messages as there is nothing defined in the app.js which says that CustomerController will be handling any request with ‘/customers’ base URI path. So we need to add some code to the apps.js to do so.

As mentioned in the controller’s spec file, the whole express application is loaded via the app.js from which we are making the HTTP post request to the base URI ‘/customers’.

Even though there is the CustomerController file available in the module, we have not explicitly defined that this controller file will be handling all the requests to the ‘/customers’ base URI in this RESTful service. So let’s instruct the app to do so with the below statements.

As usual, we can get the CustomerController module from the customer.module.js file and assign it to the variable with the same name. Let’s add this line just below the MongoDBUtil variable in the app.js file.

var CustomerController = require('./modules/customer/customer.module')().CustomerController;

Then, we can add the below line just after the MongoDB connection initialization statement.

app.use('/customers', CustomerController);

The above statement tells the express application to pass any request with the base URI ‘/customers’ path to the CustomerController router object.

Now, if we ran the Mocha test, we have passed the previous error for the status code but faced with new error as below as the next expectation is failing. Let’s continue to fix one by one until we see no more failing spec’s expectation.

Integration Test 1: CustomerController - Add Customer API - Failed 2
Integration Test 1: CustomerController – Add Customer API – Failed 2

The last piece that could get the latest error fixed is available in the middleware. We need to plug in the addCustomer() middleware function of the CustomerMiddleware in this route definition. This function will create the new customer in the MongoDB collection through the service and set the newly created customer document in the response attribute in the request.

Below is the completed code block for the test spec that we just wrote.

router.post('/',
    CustomerMiddleware.addCustomer,
    function (req, res) {
        res.status(201).json(req.response);
    });

As we have added the functional code to satisfy all the expectations of the test suite, the test should pass without any error now. Below is the screenshot of the passing test suite of the controller file so far.

Integration Test 1: CustomerController - Add Customer API - Passed
Integration Test 1: CustomerController – Add Customer API – Passed

With this integration test, we have completed the development of Add Customer API with the Test-Driven Development approach.

You can check out the below link to the source code for this step in GitHub.

GitHub – Step 05 – Add Customer API

Please check out the index page for this Node.js RESTful API development with TDD approach tutorial with all the posts in sequence in one place.

This blog post is an excerpt from the book Building Node.js REST API with TDD approach. Please check out the link for more information.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.