Alright so basically I want to make a feature for my client, but i want to make it right and put all the chances on my side, and for that, what better than some test-driven-development ?

So we already have a testing framework which is mocha and we’ll be using along with chai because the two go together so well, we will be using it to describe a batter of tests for the controller of our new feature.

RED, GREEN, YELLOW

that’s right people, we’ll do it like that, first we write a test for a feature that does not exist, meaning we will of course get an error (RED), then we will write the feature just enough so that the test passes (GREEN) and only then.. we improve the code without breaking the test and make it look pretty (YELLOW)

RED (Making a test that returns an error)

so we’ll start small and make our way up, first of all we know we’ll need an endpoint to get all of the rows that the resource’s table gives us (keep in mind that the feature that will answer this test does not exist yet, we want this test to fail !), so lets do a basic test to GET the resource’s rows, since we are using mocha and chai we import and initialize it like so

"use strict";

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

chai.use(chaiHttp);
chai.should();

(the server const is just the entrypoint of the express app)

and then we write the simple test where we describe what it does and what we expect

describe("Route GET /endpoint", () => {
  it("should return a list i guess ?", async () => {
    const res = await chai.request(server).get(`/path/to/your/endpoint`).send();

    res.should.have.status(200);
  });
});

Side note

Since im working on a existing project i have some nuances such as some jwt needed to call my api, meaning that i will add some code to actually fake-create an admin and get its jwt to make my calls before each tests, it will look like so :

const factories = require("../factories"); //custom helper
const { cleanRelationalDatabase } = require("../support/cleaners"); //custom helper

describe("API, ressource: ResourceName", () => {
  let adminParams;
  let admin;
  let jwt;

  beforeEach(async () => {
    await cleanRelationalDatabase();
    adminParams = await factories.attrs("Admin");
    admin = await factories.create("Admin");
    jwt = await admin.jwtoken();
  });

  describe("Route GET /endpoint", () => {
    it("should return a list i guess ?", async () => {
      const res = await chai
        .request(server)
        .get(`/path/to/your/endpoint`)
        .send()
        .set(`authorization`, `Bearer ${jwt}`);

      res.should.have.status(200);
    });
  });
});

you dont need to recreate this for starters, im just showing to show off some nuances.

And so upon executing we have the one and maybe only case you would be happy to see an error :

So thats just one unit test, but the more the merrier you most likely need to write more tests for each endpoint.

in the meantime we have yet to write the controller function plugged to that endpoint, so lets do just that

GREEN (Passing previously defined test)

Now that we have our failing test, it’s time to make it pass. Remember, we are not aiming for perfection yet — just enough code so that the test goes green.

Let’s say our endpoint is supposed to return all rows from a resource called ResourceName. In our controller, we might have something like this:

// controllers/resourceController.js
const { ResourceName } = require("../models");

async function getAllResources(req, res) {
  try {
    const resources = await ResourceName.findAll();
    return res.status(200).json(resources);
  } catch (error) {
    return res.status(500).json({ error: "Something went wrong" });
  }
}

module.exports = {
  getAllResources,
};

Then, in our routes file, we just plug this controller function to the endpoint:

// routes/resourceRoutes.js
const express = require("express");
const router = express.Router();
const { getAllResources } = require("../controllers/resourceController");

router.get("/path/to/your/endpoint", getAllResources);

module.exports = router;

At this point if you test again

npx mocha test/api/resource.spec.js

You’ll see all GREEN

YELLOW (Refactoring and making it look good)

Alright, now that our test passes and everything’s green, it’s time for the YELLOW phase — the sweet spot where we make our code cleaner, more maintainable, and ready for the long haul.

This step is all about refactoring without breaking anything. The rule is simple: don’t change what the code does, just how it does it. Your tests are now your safety net — if you break something, they’ll tell you immediately.

Clean up and structure our controller

// services/resourceService.js
const { ResourceName } = require("../models");

async function fetchAllResources() {
  return await ResourceName.findAll();
}

module.exports = {
  fetchAllResources,
};

Now our controller becomes thinner and more focused:

// controllers/resourceController.js
const { fetchAllResources } = require("../services/resourceService");

async function getAllResources(req, res, next) {
  try {
    const resources = await fetchAllResources();
    return res.status(200).json(resources);
  } catch (error) {
    next(error); // Let centralized error handling middleware deal with it
  }
}

module.exports = {
  getAllResources,
};

Keep the tests close and clean

Now that your structure is solid, maybe refactor the test file names or structure for better readability:

  • test/api/resource.spec.js → stays organized
  • Using clear describe blocks:
describe('GET /path/to/your/endpoint', () => {
  it('returns all resources', async () => { ... });
});

Congrats !

At this point, you’ve got:

  • RED: A failing test that defined what you needed
  • GREEN: Code that satisfies that test
  • YELLOW: A clean, maintainable version with proper structure

You didn’t just build a feature — you built confidence in your code.
Next time you add or refactor something, you’ll do it faster, safer, and with way less anxiety.