Customer Module Setup – Node.js API with TDD Tutorial

Now let’s start with the customer module. In this module, we will write the functional code needed for our API. We will apply the Test Driven Development approach to develop every piece of code to accomplish the task. Just like the previous module, we will begin by setting up the directories and files for the module. Also the initial code for each of those files as per each one’s type.

Module Files Setup

As we have already set up the customer directory (modules/customer) in the Development Environment Setup post, let’s create the below list of files in the same directory as described in design related post and we will go over these files and why we need those files in the following sections.

  1. customer.module.js
  2. customer.controller.js
  3. customer.middleware.js
  4. customer.service.js
  5. customer.model.js

1. customer.module.js file 

It is the main file for the customer module which will be used by other modules to get the reference for this module’s files. So that other modules will have to load only this file instead of few or all the files as needed. 

There is one difference between this module file and the previous mongodb module file. This file will load a function to the module object. The reason is, as this file will be loaded within other files of this module to get the handle for rest of the files. 

So it needs to be exposing a function which will return an object with all other files as an attribute upon calling this function. We will see in action why we need this file to be a function loaded as module object while developing this file with more functionalities.

2. customer.controller.js file

The entry point for any request will be this file. As it will expose the Router object to as the module object which will route the requests as defined in this object. Routing means how the application responds to the client request to a particular endpoint (URI or path) and a specific HTTP method (GET, POST, PUT or DELETE). 

The URI for all the endpoints will be defined in this file along with one or more handler functions. So when a request’s URI matches any of the route definitions will be handled by the handlers associated with the route definition. 

Each route definition will have a final handler to process the result from the other previous handler functions and send the response back to the client. The handler functions could be a middleware function which has access to request, response objects and next function.

So the request will cascade through these handler functions and finally return the response object with the result as per the request.

3. customer.middleware.js file

To handle the customer related paths or URIs, we will create the middleware functions in this file. As you know already, middleware functions have access to the request, response objects and next function to invoke other middleware functions in the stack. 

One of the primary purposes of the middleware is to change the request and response objects as needed. So, this file’s middleware functions will read the required query, parameters and body objects from the request and call the functions of the service files to execute the code necessary to accomplish the requested task.

Once the code execution is complete and appropriate result has been obtained, the middleware could end the request-response cycle after placing the result in the response object to be sent to the client.

If there are different tasks need to be completed for the request as per the middleware functions associated with the path in the routing definition, next middlewares will be invoked to perform their tasks. While doing so, these middlewares can place some data in the request object to share the data across the middlewares in the stack. The last middleware in the stack would end the request-response cycle unless there is no error thrown the any of the previous middlewares.

4. customer.service.js file

Even though the middleware functions can directly interact with the file systems/databases to add/modify/fetch the data, it would clutter the middleware function as it would end up doing more than one thing unnecessarily. We could offload the file system/database interaction to another file which we can call service. 

This way we can keep the lines of code in each of these files’ functions as less as possible to make it more readable and maintainable. By doing so, will also make it more modular. Another benefit we could get from this approach is that the middleware will have to parse the request object to extract the required information to be passed to the service functions and also deal with the response objects as well.

Also, the service functions will not have access to the request and response objects but the required information which is passed as the parameters.

5. customer.model.js file

Since we use MongoDB document database for storing the customer information, we need to define the shape of the document. It is the file where we specify the properties of the customer document and its datatypes. 

We will need the mongoose to handle all the MongoDB related interaction. In mongoose, everything starts with the definition of the Schema for the data that we want to store in the MongoDB collection. The schema defines the shape of the document, and it’s compiled to create the Model class for the customer details. The instance of the Customer Model class is the one that we store in the collection as a Customer Document.

In this file, the Customer Model class is exported as a module, and it’s used to create, update, delete and fetch the customer documents in the MongoDB database.

Segregation of Duties

With these different files, there is a clear segregation of duties are well defined as below.

  1. customer.module.js:  Main javascript file where all the module files are exposed as attributes in the object which can be used to load each file in other modules’ files.
  2. customer.controller.js: This is an express router exposed as a function with the router for each path or URI is well defined along with handler middleware functions. Request to a particular endpoint is handled here with appropriate routing to serve that request.
  3. customer.middleware.js: Handling of request and response objects are performed at this file. Also, the requested action is accomplished within each of the middleware functions by calling service functions with required parameters which are extracted from the request object at this layer.
  4. customer.service.js: The functions available in this file deal with the actions for creating, updating deleting and fetching the document through the Model. It is an intermediary between the controller and model to preprocess the documents for the required operation.
  5. customer.model.js: The interaction between then MongoDB is happening here with the help of mongoose module. All the document creation, updating, deletion and retrieval from the database is handled by the models which are exported as the module by this file.
Segregation of Duties - Customer Module - Node.js API with TDD Tutorial
Segregation of Duties – Customer Module

Test Files Setup

For the files available in this module, we will create the spec files. Although, we won’t be writing any unit test scripts for the controller and model files. Like unit testing, the model file may not be needed as we will be adding required tests in the service spec itself. 

Also for the controller file, we may not be successfully able to write the specs to test it’s behaviour with appropriate mocking or stubbing the dependent functions. However, we will be writing integration test specs in the coming post which will be examining the behaviour of the controller file itself along with all the files in the layers as a whole.

So, for now, below test files will be created under the tests/unit/customer directory.

  1. customer.module.spec.js
  2. customer.middleware.spec.js
  3. customer.service.spec.js

Test Fixtures Setup

While we test the functionality with the unit test scripts, we would eventually use a sample data set to pass as inputs and confirm the outputs. We could create these data inside the specs themselves. However, this approach will clutter the test script and will become cumbersome to maintain it if the testing data grows more substantial over time.

There is a way to avoid this issue and keep the file clean for only the testing scripts. The test data can be maintained outside of the specs and loaded whenever it’s needed upon running the test. The files that hold the test data is called fixtures. As we will be writing the API code for almost all the HTTP methods, the input and output data will become more than we want to maintain each of the spec itself.

We will create these files with empty JSON format ({ }) for now and fill in with appropriate content in JSON format while we go through each of the tests. So below are the fixtures that we would need for the testing. Let’s create these files in the tests/fixtures directory. All the files mentioned below will have to have only the empty JSON format with just { }string as content.

tests/fixtures/customer directory

These files contain the test data needed for the customer module files testing.

  1. new-customer.json
  2. created-customer.json
  3. modified-customer.json
  4. customers.json

tests/fixtures/error directory

Since we will also test the negative test scenarios with an error thrown from any of the layer, error related test data will be required as well. Content for these files will also be filled in the coming sections.

  1. error-404.json
  2. error-unknown.json

Fixture Scripts

Apart from the JSON files, we will need a few more javascript files which will be exposing these test files as attributes of an object. So that we can avoid requiring individual files separately with its full path wherever it’s needed. Just like mongodb.module file lets create the below files under the specified directories as below.

  1. tests/fixtures/customer/customer-fixture.js
  2. tests/fixtures/error/error-fixture.js
  3. tests/fixtures/fixtures.js

1. tests/fixtures/customer/customer-fixture.js file

This file will expose all the test fixtures from the tests/fixtures/customer/ directory. The content of this file will be as follows. 

(function () {
    'use strict';

    module.exports = {
        customers: require('./customers.json'),
        newCustomer: require('./new-customer.json'),
        createdCustomer: require('./created-customer.json'),
        modifiedCustomer: require('./modified-customer.json')
    };

})();

2. tests/fixtures/error/error-fixture.js file

To expose error related test fixtures from the tests/fixtures/error/directory, we will use this file with the below content.

(function () {
    'use strict';

    module.exports = {
        unknownError: require('./error-unknown.json'),
        error404: require('./error-404.json')
    };

})();

3. tests/fixtures/fixtures.js file

Finally, as might be adding more modules for the API, the number of fixtures javascript files will also be increased. So we might end up requiring these javascript files with their full path in the respective test module files.

We could even make this simpler by creating another high-level fixture javascript files to expose all the module level fixture javascript files. The fixtures.js file under the tests/fixtures will be used for that purpose. 

Here is the content of this file which exposes the attributes with the appropriate module level fixture javascript files for quick and easy access to the need to know those files’ complete path.

(function () {
    'use strict';

    module.exports = {
        CustomerFixture: require('./customer/customer-fixture'),
        ErrorFixture: require('./error/error-fixture')
    };

})();

With this, we have completed all the test fixtures that we would need for all the specs of this RESTful Service. Now, we will start with some initial specs for the customer module, and we will add more tests as we go further.

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.