DEV Community

Cover image for Testing Node API with Mocha & Chai
Ekunola Ezekiel
Ekunola Ezekiel

Posted on • Updated on • Originally published at blog.ezekielekunola.com

Testing Node API with Mocha & Chai

What is Mocha?

Mocha is a feature-rich JavaScript test framework running on Node.js and in the browser, making asynchronous testing simple and fun.

  • Synchronous Test Example
it('two plus two is four', () => {
    expect(2 + 2).to.equals(4);
  });
Enter fullscreen mode Exit fullscreen mode
  • Asyncronous Test Code
it("adds 2 numbers", (done) => {
   // perform asynchronous actions
   // write tests
    done(); // call the done function after test.
  });
Enter fullscreen mode Exit fullscreen mode

What is Chai?

Chai is a BDD (Behaviour-Driven Development) / TDD (Test-Driven Development) assertion library for nodejs and the browser that can be delightfully paired with any javascript testing framework.

Assertion Types

Chai has several interfaces from which developers can choose from. They are:

chai.should();
foo.should.be.a('string'); 
foo.should.equal('bar');
foo.should.have.lengthOf(3);
tea.should.have.property('flavors').with.lengthOf(3);
Enter fullscreen mode Exit fullscreen mode
var expect = chai.expect;  
expect(foo).to.be.a('string'); 
expect(foo).to.equal('bar'); 
expect(foo).to.have.lengthOf(3); expect(tea).to.have.property('flavors').with.lengthOf(3);
Enter fullscreen mode Exit fullscreen mode
var assert = chai.assert;  
assert.typeOf(foo, 'string'); 
assert.equal(foo, 'bar'); 
assert.lengthOf(foo, 3);
assert.property(tea, 'flavors'); 
assert.lengthOf(tea.flavors, 3);
Enter fullscreen mode Exit fullscreen mode

Server Test

Now, I would be taking us through the process of setting up a basic Node API and writing tests for it.

First thing is to create our project folder and initialize it with npm to generate the package.json file.

npm init -y
Enter fullscreen mode Exit fullscreen mode

Install dependencies by running the commands below on your terminal

npm i express --save
npm i mocha chai chai-http --save-dev
Enter fullscreen mode Exit fullscreen mode

Setup Server

Create an app.js file in the root of your project and add code below to create a server with express and also endpoints that we would be testing.

const express = require("express");

const app = express();

app.use(express.json());

app.get("/", (req, res) => {
  res.json({ status: "success", message: "Welcome To Testing API" });
});

app.post("/add", (req, res) => {
  const { num1, num2 } = req.body;
  const add = (num1, num2) => {
    return num1 + num2;
  };
  res.json({
    status: "success",
    result: "Welcome To Testing API",
    result: add(num1, num2)
  });
});

const PORT = process.env.PORT || 3000;

app.listen(PORT, () => console.log(`App listening on port ${PORT}`));

module.exports = app;

Enter fullscreen mode Exit fullscreen mode

Setup Scripts

Add test script to package.json file.

"scripts": {
    "test": "mocha"
  }
Enter fullscreen mode Exit fullscreen mode

Write Tests

Create a test directory in your project, and then create an app.test.js file inside the test directory created.

Note: The reason we're adding our tests to the test directory is that mocha searches for a test directory in your project by default, although this can be configured to suit your style. Find more here

Then add code below

const app = require("../app");
const chai = require("chai");
const chaiHttp = require("chai-http");

const { expect } = chai;
chai.use(chaiHttp);
describe("Server!", () => {
  it("welcomes user to the api", done => {
    chai
      .request(app)
      .get("/")
      .end((err, res) => {
        expect(res).to.have.status(200);
        expect(res.body.status).to.equals("success");
        expect(res.body.message).to.equals("Welcome To Testing API");
        done();
      });
  });

  it("adds 2 numbers", done => {
    chai
      .request(app)
      .post("/add")
      .send({ num1: 5, num2: 5 })
      .end((err, res) => {
        expect(res).to.have.status(200);
        expect(res.body.status).to.equals("success");
        expect(res.body.result).to.equals(10);
        done();
      });
  });
});

Enter fullscreen mode Exit fullscreen mode

At this point, your folder structure should look like the image below
Folder Structure

To run the test, run the command below on your terminal.

npm test
Enter fullscreen mode Exit fullscreen mode

Once the test is run, if the test passes, you should get a successful response on your terminal like the example shown below
Success Test

...and if the test fails, an error response on your terminal like the example shown below
Error Test

Find the code used in this project on Github
Find more information on Mocha here
Find more information on Chai here

Conclusion

In this article, we have been able to look at setting up testing with Mocha and Chai and also writing tests for our Node API. I would be improving on this article and project to integrate a Continuous Integration tool called Travis-CI.

If you have any questions or feedback about this article, feel free to leave a comment.
Thanks for reading.

Top comments (16)

Collapse
 
nivedithamnj profile image
nivedithaMNJ

How can i store value from one API call & use it in next ?

Collapse
 
easybuoy profile image
Ekunola Ezekiel

You can nest API calls, an example below

chai.request(app).post('/api/v1/auth/login')
      .send({
        email: 'example@gmail.com', password: '123456',
      })
      .end((err, res) => {
        const { token } = res.body.data;
        storeownertoken = token;

        chai.request(app).post('/api/v1/auth/login')
          .send({
            email: 'example2@gmail.com', password: '123456',
          })
          .end((err2, res2) => {
            storeattendanttoken = res2.body.data.token;
            chai.request(app).post('/api/v1/auth/login')
              .send({
                email: 'example31@gmail.com', password: '123456',
              })
              .end((err3, res3) => {
                undefinedtypetoken = res3.body.data.token;
                done();
              });
          });
      });

I hope it makes sense?

Collapse
 
nivedithamnj profile image
nivedithaMNJ • Edited

Hi! Thank you For responding, but it is still not working for me, the variable I am storing from the first API call turns out to be null when accessed in 2nd API call.
My Scenario: 1st Post call - Get an access token which I am successfully able to capture , 2nd Post call - Use this access token to view authenticated data.

Thread Thread
 
easybuoy profile image
Ekunola Ezekiel

Yeah, your process seems right, not quite sure why you're getting null. Can I see a screenshot of the code?

Thread Thread
 
nivedithamnj profile image
nivedithaMNJ • Edited

This is what i have.. i am not sure what I am doing wrong.
Here I do get the token from Authurl but when I access it for next call .. i don't get the token hence it fails
---- Code --

var authtoken ="";
describe('/Get Welcome Message', () => {
it('Generate Auth token', (done) => {
chai.request(testConfig.baseUrl)
.post(testConfig.authUrl)
.send(testConfig.loginCred)
.set('_format', 'json')
.end((err, res) => {
expect(err).to.be.null;
authToken = res.text.slice(10,-21);
console.log("authToken : "+JSON.stringify(res.text.slice(10,-21)));
expect(res).to.have.status(200);
chai.request(testConfig.baseUrl)
.get(testConfig.cardsDefaultsUrl)
.set('Authorization',authtoken)
.set('Content-Type','application/json')
.set('componentName', 'banner')
.set('_format', 'json')
.end((err, res) => {
console.log("Token : "+authtoken);
expect(err).to.be.null;
expect(res).to.have.status(200);
console.log("Response : "+JSON.stringify(res));
});
done();
});
});
----- end ---

Thread Thread
 
easybuoy profile image
Ekunola Ezekiel

Okay, have you tried logging the error you're getting on the second API call to see what the error is?

Thread Thread
 
nivedithamnj profile image
nivedithaMNJ • Edited

Yes ...when i log the err .. its null.
But the response shows a 401 , which is an authentication error. Also I print the auth token which turns out to be null. Hence I believe the token is not getting passed to the 2nd API .

Thread Thread
 
easybuoy profile image
Ekunola Ezekiel • Edited

That's wired, let's try making the first request in a before hook, and then use the token where needed.

You can search here and see an implementation for hooks: mochajs.org/

Thread Thread
 
nivedithamnj profile image
nivedithaMNJ

Hi! I have the code working now but the before/before each didn't seem to work as expected. I realized I was making a mistake while parsing the JSON response.
Thanks a lot for helping.

Collapse
 
johndavemanuel profile image
John Dave Manuel

Great tutorial! What icons are you using on the screenshot of the file structure?

Collapse
 
easybuoy profile image
Ekunola Ezekiel

Thanks, John. I installed a vs code extension called Material Icon Theme.

Collapse
 
johndavemanuel profile image
John Dave Manuel

Thanks! I will give it a try.

Collapse
 
nivedithamnj profile image
nivedithaMNJ

Hi!
I have an issue that I face with my tests, when I run the tests it doesn't exit after execution hence I added --exit to my mocha command & it worked as expected.
But now due to a requirement, my code gets picked up by jest for the execution of unit tests + my API test hence run by the command "jest --coverage" and everything gets stuck because the mocha code that gets picked up for execution doesn't exit.
Please let me know if you have any suggestions on the same.

Collapse
 
youngeinstein10 profile image
Abdulrahman Yusuf

Thanks for this, i really love the analogy you used in explaining it

Collapse
 
easybuoy profile image
Ekunola Ezekiel

I'm glad you enjoyed it.

Collapse
 
drozerah profile image
Drozerah • Edited

Hi! Ez.

Do you provide test to check if an HTML element is DOM injected after a GET ?

It would be great to read you about that ;)