loading...

Setup Express API Test with Mocha and Chai using Typescript

nabinadhikari profile image Nabin Adhikari Updated on ・3 min read

Today, I'll be explaining how we can easily set up the testing environment
for the development of an Express API app. In this setup, we will be using Mocha and Chai with Typescript.

So, Let's get started.

First things first. Let's get our basic project setup. We will be using NPM here, but you can use Yarn as well (if you prefer it).

$ npm init -y

This should create a package.json file in your current working directory. Next, we install express, body-parser, and mongoose for creating a meaningful API using the following command.

npm i -S express body-parser mongoose

Now let's install all the dependencies only we care about (devDependencies).

npm i -D typescript chai mocha nodemon ts-node supertest​

Let's install types for all our dependencies as well. Anyway, How good is typescript without types?

​​​​​​​npm i -D @types/node @types/mocha @types/express @types/chai @types/supertest @types/mongoose

Setup Typescript

Now let's setup typescript. Just create ​tsconfig.json file at the root directory and copy the following config.

{
    "compilerOptions": {
        "target": "es2017",
        "module": "commonjs",
        "outDir": "dist",
        "sourceMap": true
    },
    "include": [
        "src/**/*.ts"
    ],
    "exclude": [
        "node_modules",
        "test/**/*.ts"
    ]
}

Test script

In our package.json file let's add a script test-dev to run it continuously every time we make any changes in our codebase. Simply put the following script in the scripts section of package.json.

"test-dev": "nodemon --watch . --ext ts --exec \"mocha -r ts-node/register test/**/*.test.ts\""

What this does is ask nodemon to watch all of the folders and run Mocha if it finds any file changes.

Now, let's create a test directory in the root directory and create index.test.ts inside it, then add the following scripts.

import {expect} from 'chai';
describe("Index Test", () => {
    it('should always pass', function () {
        expect(true).to.equal(true);
    });
});

Next, execute npm run test-dev to start the test. You should see one test suite passing. Congratulation, you have successfully created your test.

Now, let's add a couple of tests first to test our express app.

it('should POST /api/todo v2', async function () {
    const res = await request(app)
        .post('/api/todo').send({todo: "first todo"});
    expect(res.status).to.equal(200);
    expect(res.body).not.to.be.empty;
    expect(res.body.data).not.to.be.empty;
    expect(res.body.data).to.be.an("object");
    expect(res.body.error).to.be.empty;
});
it('should GET /api/todo', async function () {
    const res = await request(app).get('/api/todo');
    expect(res.status).to.equal(200);
    expect(res.body).not.to.be.empty;
    expect(res.body.data).not.to.be.empty;
    expect(res.body.data).to.be.an("array");
    expect(res.body.error).to.be.empty;
});

Curious where request and app comes from? Import them like this.

import app from '../src/app';
import {agent as request} from 'supertest';

These couple of tests are testing GET and POST for route /api/todo. We know we haven't created express yet, which we will create next. If you have tests running you should see a big error complaining about everything. We aim to fix those error by the end of this post.

Setup Express App

We create index.ts file inside src folder and add the following scripts.

import * as express from 'express';
import {Todo} from "./models";
import * as mongoose from 'mongoose';
import {json} from 'body-parser';

const app = express();
app.use(json());

if (mongoose.connection.readyState === 0) {
    mongoose.connect('mongodb://localhost:32768/test')
        .then(() => console.log('Db connected...'))
        .catch(e => console.log("Db connection error", e));
}
app.get('/api/todo', async (req, res) => {
    try {
        const todos = await Todo.find({});
        res.status(200).json({error: null, data: todos});
    } catch (e) {
        res.status(500).json({error: e.message, data: null});
    }
});
app.post('/api/todo', async (req, res) => {
    try {
        const todo = await new Todo({todo: req.body.todo}).save();
        res.status(200).json({error: null, data: todo});
    } catch (e) {
        res.status(500).json({error: e.message, todo: null});
    }
});
export default app;

It might look huge file to copy, but essentially all this is doing is creating an express with a single route /api/todo for GET and POST method to create a todo (POST) and return all of the todos (GET).

If you are curious where the Todo comes from, it is a mongoose model create in Todo.ts with following contents.

import * as mongoose from 'mongoose';
const Todo = mongoose.model('todo',
    new mongoose.Schema({
            todo: String,
            complete: {
                type: Boolean,
                default: false
            }
        }
    )
);
export default Todo;

This way, we can follow Test Driven Development (TDD) approach. I've written this post to help others get started quickly, as it took me quite a bit of time to figure this out. Please feel free to correct me if I've mistaken about anything.

Posted on by:

nabinadhikari profile

Nabin Adhikari

@nabinadhikari

Enthusiastic Fullstack Developer with experience in React, Vue, Dotnet, NodeJs and occasional Android. Visit https://nabinadhikari.com/

Discussion

markdown guide
 

This has multiple mis-matches and one plain error. I'll list them in order of appearance:

  • Mismatch of import app from '../src/app'; with naming the file index.ts.
  • Error: import * as express from 'express'; should be import express from 'express'; otherwise you create a naming conflict (and no express.express() doesn't work).
  • Mismatch of import {Todo} from "./models"; with naming the file Todo.ts.
  • Mismatch of import {Todo} from "./models"; with export default Todo;.

:Ü™