Get Customer List API – CustomerMiddleware – Get Customer List Unit Tests – Node.js API with TDD Tutorial

In the last post, we have seen how to write unit tests and functional code for the CustomerService component’s fetchCustomers() function. Let’s continue with the next component in the layers, Middleware, now.

Customer Middleware – Get Customers Unit Tests

Let’s continue with the middleware method testing and development in this section. Here, we will have to write the test suite for the CustomerMiddleware’s getCustomers() function, as per the low-level design.

Unit Test 17: GetCustomers Success Test Case

For this new unit test case, we will start with setting up the test suite, then test script and finally, the code to satisfy the middleware function’s test spec.

Test Suite

There will be a new suite added in the customer.middleware.spec.js file with the describe() block as below to begin with.

describe('getCustomers', function () {
});

Inside this describe block, we need to add some variables declarations and preconditions set up and clean up blocks as well. As we are utilizing the stubbing approach for setting up the dependent CustomerService for the middleware module for the previous test suite addCustomer,’ we will continue to use the same for this suite. So, below will be the updated describe() block with the changes mentioned above applied.

describe('getCustomers', function () {
    var fetchCustomers, fetchCustomersPromise, expectedCustomers, expectedError;

    beforeEach(function () {
        fetchCustomers = sinon.stub(CustomerService, 'fetchCustomers');
        req.body = {};
    });

    afterEach(function () {
        fetchCustomers.restore();
    });

});

In the first line, we are declaring the variables for the stubbed method, and its promise function output. Also, we have the variables for the output data for the successful execution path testing and error data for the failed execution path testing. 

The next code block, beforeEach(), is creating the stub method fetchCustomers() for the CustomerService, which is a dependency for the current method under the test. Also, the cleanup function, afterEach(), comes, at last, to restore the stubbed method of the service module once each test execution is finished.

Test Script

 To start the first middleware test for the getCustomers() method, let’s create it() block for the successful execution path test spec as below with complete script.

it('should successfully get all customers', function () {
    expectedCustomers = CustomerFixture.customers;

    fetchCustomersPromise = Promise.resolve(expectedCustomers);
    fetchCustomers.returns(fetchCustomersPromise);

    CustomerMiddleware.getCustomers(req, res, next);

    sinon.assert.callCount(fetchCustomers, 1);

    return fetchCustomersPromise.then(function () {
        expect(req.response).to.be.a('array');
        expect(req.response.length).to.equal(expectedCustomers.length);
        expect(req.response).to.deep.equal(expectedCustomers);
        sinon.assert.callCount(next, 1);
    });

});

In this spec, we are following the same steps as other specs that we have written so far. The expectedCustomers variable can be pre-populated with the test data from CustomerFixture.

 Since the middleware method getCustomers() will be receiving a promise from the service method fetchCustomers() when it’s called, we are adding the expected behavior to the stubbed method with the below lines code. A promise is created using the Promise object of the bluebird module by setting it up to resolve and return the expectedCustomers test data when the dependent method call completed.

fetchCustomersPromise = Promise.resolve(expectedCustomers);
fetchCustomers.returns(fetchCustomersPromise);

Next, we are invoking the method under test, getCustomers() of the CustomerMiddleware, to test its behavior. Right after that, we are asserting that the dependent method, fetchCustomers stub, was called only once. 

CustomerMiddleware.getCustomers(req, res, next);
sinon.assert.callCount(fetchCustomers, 1);

Finally, to confirm and validate the expected behavior of the service method’s successful path, the then() block of the returned promise, we have added expectations of the data returned inside the same block. Here, we need to check that the returned data is an array of the customers and it’s of the same length and same data structure with values as the test data. The invocation of the next() function can also be confirmed within this assertion block.

return fetchCustomersPromise.then(function () {
    expect(req.response).to.be.a('array');
    expect(req.response.length).to.equal(expectedCustomers.length);
    expect(req.response).to.deep.equal(expectedCustomers);
    sinon.assert.callCount(next, 1);
});

It completes our spec for the successful execution path of the getCustomers() middleware function. The below would be the test result when running the test suites at this moment. To solve this issue, we need to continue with the next section.

Unit Test 17: CustomerMiddleware - getCustomers() - Success Test Case - Failed 1
Unit Test 17: CustomerMiddleware – getCustomers() – Success Test Case – Failed 1

Code

Whenever we encounter the error which says that a method is not a function of the module that we are testing against, the first step to do is creating the named method with empty content and expose it from the module itself. Just like the below scripts for the customer.middleware.js file. These scripts will be added at the appropriate locations inside the js file.

module.exports = {
    addCustomer: addCustomer,
    getCustomers: getCustomers
};

function getCustomers(req, res, next) {
}

As the getCustomers() is a middleware function, it needs the request, response, and next as inputs to comply with its nature. This time when you run the tests will yield new error as below as the method is not complete yet.

Unit Test 17: CustomerMiddleware - getCustomers() - Success Test Case - Failed 2
Unit Test 17: CustomerMiddleware – getCustomers() – Success Test Case – Failed 2

For the new error, adding the script to call the service method fetchCustomers() with promise handling method blocks would be required. 

The handler function for the promise’s successful response will assign the returned the fetched data to the response attribute in the request which will be used in the controller layer. Then it will have to invoke the next() function to continue with the request-response lifecycle. 

Here is the completed getCustomers() method with all the expected behavior is coded.

function getCustomers(req, res, next) {

    CustomerService.fetchCustomers()
        .then(success);

    function success(data) {
        req.response = data;
        next();
    }
}

As all the required functionality of the method under the test is completed, now we should be getting the successful test run with below results for the CustomerMiddleware’s test specs. 

Unit Test 17: CustomerMiddleware - getCustomers() - Success Test Case - Passed
Unit Test 17: CustomerMiddleware – getCustomers() – Success Test Case – Passed

Unit Test 18: GetCustomers Failure Test Case

Now we will go through the failure execution path of the getCustomers() middleware function testing. With this test, all the unit test cases for the get customers list functionality will be completed.

Test Script

Let’s add it() block for this purpose stated in its description as below. We need to populate the test data expectedError from the ErrorFixture’s unknownError json object. When we set up the behavior for the stubbed function fetchCustomers() of the service module, we need to make sure that its promise is rejecting with the error object. 

Next would the same step as the success path, that’s calling the getCustomers() function and asserting the one call count of the fetchCustomers() method. Finally, we will be comparing the returned error object from the invoked method with the expectedError variable. All these steps are written as the script as below.

it('should throw error while getting all customers', function () {
    expectedError = ErrorFixture.unknownError;

    fetchCustomersPromise = Promise.reject(expectedError);
    fetchCustomers.returns(fetchCustomersPromise);

    CustomerMiddleware.getCustomers(req, res, next);

    sinon.assert.callCount(fetchCustomers, 1);

    return fetchCustomersPromise.catch(function (error) {
        expect(error).to.be.a('object');
        expect(error).to.deep.equal(expectedError);
    });

});

When we run the test suites we should see an error message as below which is because we haven’t written the catch() block inside the middleware function to capture the error from the service method call. We will fix this issue in the coming section. Here is the error that we will see now.

Unit Test 18: CustomerMiddleware - getCustomers() - Failure Test Case - Failed
Unit Test 18: CustomerMiddleware – getCustomers() – Failure Test Case – Failed

Code

To resolve the unhandled rejection error for the getCustomers() method call,  we need to add the catch() block which will handle the promise’s rejected path and set the error object in the next() call itself so that the error will be processed at the application level. If you add the necessary script for this error capturing part the completed code will be looking as shown below.

function getCustomers(req, res, next) {

    CustomerService.fetchCustomers()
        .then(success)
        .catch(failure);

    function success(data) {
        req.response = data;
        next();
    }

    function failure(err) {
        next(err);
    }

}

Here is the final test result’s screenshot the next test run will show after the above code is written.

Unit Test 18: CustomerMiddleware - getCustomers() - Failure Test Case - Passed
Unit Test 18: CustomerMiddleware – getCustomers() – Failure Test Case – Passed

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.