Photo by icon0.com from Pexels

You have spent a lot of time and effort writing up a fairly medium application, let’s say with a code base of about 1000 lines and you’ve manually tested the application to make sure everything is running fine. You push your code to Github and someone decides to contribute their own quota to your work. They push their code, created a pull request and you merge it in, now your application no longer runs, everything is broken, all because of the code you merged in. In order to avoid this type of problem and many more that come with software development, you need to integrate testing to your workflow.

Testing requires that you write tests which cover various input a software might receive and the corresponding output to them. That way you can be sure that the application is running exactly how you intended it to and this can prevent a lot of bugs. It is always important to write a test before new code is added to your code-base so you can be sure the new code is not introducing any bug to your codebase and also it helps you know beforehand if the new code is breaking any part of your codebase.

In this article, we’ll be writing a simple Node/Express API application while incorporating testing using the mocha & chai JavaScript testing packages.

Mocha, according to the description on its website, is a testing framework that makes asynchronous testing simple and fun. It will provide a testing environment that makes it easy for us to run chai.

Chai is an assertion library that can be paired with any test framework. It is the library that we’ll actually write our tests with.

Setting up our sample application

We’ll be building out an application that reads information from non-persistent students record data. To continue, we need to have the following files and folders created:

---controllers/
------studentController.js
---dummy/
------students.js
---routes/
------index.js
---tests/
------test.js
---.babelrc
---server.js
---package.json

To set up our dummy data, we need to include the data in the dummy/students.js file:

const students = [
   {
     id: 1,
     name: 'Sean Grey',
     age: 24,
   },
   {
     id: 2,
     name: 'John Doe',
     age: 26,
   },
   {
     id: 3,
     name: 'Janet Dane',
     age: 19,
   },
];
export default students;

The code block above assigns an array of objects, each object holding the details of a student.

Now let’s set up our package.json, so we can install all the packages we would be needing to build out and test our application.

{
   "name": "student-record",
   "version": "1.0.0",
   "description": "An API to manage student record",
   "main": "server.js",
   "author": "Samuel Afolaranmi",
   "license": "MIT",
   "scripts": {
        "test": "mocha --require babel-register tests/*.js --exit",
        "dev": "nodemon --exec babel-node --presets babel-preset-env ./server.js"
   }
   "dependencies": {
        "body-parser": "^1.18.3",
        "express": "^4.16.3"
   },
   "devDependencies": {
        "babel-cli": "^6.26.0",
        "babel-preset-env": "^1.7.0",
        "chai": "^4.1.2",
        "chai-http": "^4.0.0",
        "mocha": "^5.1.1",
        "nodemon": "^1.17.4"
   }
}

In the package.json file, we include our mocha and chai, which we’ll be using to write our tests. We also needed to include chai-HTTP which is a plugin that allows us to run HTTP integrations with chai assertions. We can now run npm install to install the packages and get ready to finish setting up our application.

The next step is to create our routes and server.js files, but first, we should create our controller file as we would be needing to import it into our routes file. In the controllers/studentController.js file, we should include:

import students from '../dummy/students.js';
class StudentController {
    // Get all students
    static getAllStudents(req, res) {
          return res.status(200).json({
                students,
                message: "All the students",
          });
    }
    // Get a single student
    static getSingleStudent(req, res) {
           const findStudent = students.find(student => student.id === parseInt(req.params.id, 10));
           if (findStudent) {
               return res.status(200).json({
                     student: findStudent,
                     message: "A single student record",
               });
           }
           return res.status(404).json({
                 message: "Student record not found",
           });
    }
}
export default StudentController;

In the controllers/studentController.js file, we imported our dummy data, created a class to hold our controller methods and created two static methods each for what we want to achieve with the controller class. The first method, getAllStudents, as the name implies gets all the students record we have in our dummy data and returns them with a 200 HTTP status code, while the second method, getSingleStudent, gets the record of a single student and returns it with a 200 HTTP status. If a record is not found, a 404 HTTP status code is returned.

Now that we have our controller set up, we can now go back to working on our routes and server.js. In our routes/index.js file, we should add the following code:

import StudentController from '../controllers/studentController.js';
const routes = Router();
routes.get('/', StudentController.getAllStudents);
routes.get('/:id', StudentController.getSingleStudent);
export default routes;

We imported Router (express router) from express and assigned it to routes, we also imported our StudentController class from our ```controllers/studentController. js``` file. We used the Router we imported to create two routes, which are tied respectively to their corresponding controller methods.

Now we should create our ```server.js``` file so we can test the code we’ve been writing if it works.

import express from 'express';
import bodyParser from 'body-parser';
import routes from './routes/index';

// Instantiate express
const app = express();

// Set our port
const port = process.env.PORT || 8000;

// Configure app to user bodyParser
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());

// Register our routes in app
app.use('/', routes);

// Start our server
app.listen(port, () => {
    console.log(`Server started on port ${port}`);
});
// Export our app for testing purposes
export default app;

Because we’re writing ES6 code, we need babel to compile our code, and for that to work, we need to add the following code to our .babelrc file:

{
   "presets": ["env"]
}

Now that we have our application all set up, we can go ahead to run npm run dev to run our application and test our endpoints using Postman.

Writing tests for our application

Our application works well, but we need to write tests for it. To make sure we don’t break it, while also covering all edge cases. In our tests/test.js file, we’ll write our tests.

// Import the dependencies for testing
import chai from 'chai';
import chaiHttp from 'chai-http';
import app from '../server';
// Configure chai
chai.use(chaiHttp);
chai.should();
describe("Students", () => {
    describe("GET /", () => {
        // Test to get all students record
        it("should get all students record", (done) => {
             chai.request(app)
                 .get('/')
                 .end((err, res) => {
                     res.should.have.status(200);
                     res.body.should.be.a('object');
                     done();
                  });
         });
        // Test to get single student record
        it("should get a single student record", (done) => {
             const id = 1;
             chai.request(app)
                 .get(`/${id}`)
                 .end((err, res) => {
                     res.should.have.status(200);
                     res.body.should.be.a('object');
                     done();
                  });
         });
         
        // Test to get single student record
        it("should not get a single student record", (done) => {
             const id = 5;
             chai.request(app)
                 .get(`/${id}`)
                 .end((err, res) => {
                     res.should.have.status(404);
                     done();
                  });
         });
    });
});

At the beginning of the file, we imported all packages needed to make the test run, then we configured chai to use the chai-HTTP plugin. We also configured chai to use the should interface by running chai.should() . Each describe blocks, are used to group our tests together for easier access and better organisation.

The first it block is a test that runs on the first endpoint to get all student record from the data, it asserts that the response should have a status of 200 and it should return an object. The second it block is a test that runs on the second endpoint to get a single student request. Assuming the student exists, it asserts that the response should have a status of 200 and it should return an object. And finally, the third it block is a test that runs also on the second endpoint to get a single request. Assuming the student does not exist, it asserts that the response should have a status of 404.

All that is remaining is for us to run npm run test and we’ll see our tests passing before our very eyes. Beautiful, isn’t it?