Skip to content

End-to-End Backend Testing Setup (Jest + Supertest + Mongo) #10

@abhishek-nexgen-dev

Description

@abhishek-nexgen-dev

Our backend currently lacks a standardized, production-grade testing system.
To ensure reliability, scalability, and safe refactoring, we will implement a full testing architecture using:

  • Jest → Test runner
  • Supertest → API testing
  • Mongo Memory Server / Test DB → Isolated database

This setup will support:

  • Unit tests (functions/services)
  • Integration tests (modules + DB)
  • API tests (routes + auth)
  • CI/CD validation

🎯 Objectives

  • Establish a scalable testing architecture
  • Enable testing for all backend layers
  • Ensure isolation (no real DB pollution)
  • Enforce quality via CI/CD

⚙️ Implementation Tasks


1️⃣ Install Dependencies

npm install -D jest ts-jest @types/jest supertest mongodb-memory-server

2️⃣ Configure Jest

📄 jest.config.ts

export default {
  preset: "ts-jest",
  testEnvironment: "node",
  testMatch: ["**/tests/**/*.test.ts"],
  moduleFileExtensions: ["ts", "js"],
  clearMocks: true,
  setupFilesAfterEnv: ["<rootDir>/tests/setup.ts"],
};

3️⃣ Setup Test Environment

📄 tests/setup.ts

import mongoose from "mongoose";
import { MongoMemoryServer } from "mongodb-memory-server";

let mongo: MongoMemoryServer;

beforeAll(async () => {
  mongo = await MongoMemoryServer.create();
  const uri = mongo.getUri();
  await mongoose.connect(uri);
});

afterEach(async () => {
  const collections = mongoose.connection.collections;
  for (const key in collections) {
    await collections[key].deleteMany({});
  }
});

afterAll(async () => {
  await mongoose.connection.close();
  await mongo.stop();
});

4️⃣ Update Scripts

📄 package.json

{
  "scripts": {
    "test": "jest",
    "test:watch": "jest --watch",
    "test:coverage": "jest --coverage"
  }
}

📁 Folder Structure

src/
 ├── controllers/
 ├── services/
 ├── models/
 ├── routes/
 ├── utils/

tests/
 ├── unit/
 ├── integration/
 ├── api/
 ├── setup.ts

🧪 Test Types & Examples


✅ 1. Unit Test (Functions)

📄 tests/unit/math.test.ts

import { describe, test, expect } from "@jest/globals";
import { square } from "../../src/utils/math";

describe("Math Utils", () => {
  test("square should return correct value", () => {
    expect(square(4)).toBe(16);
  });
});

✅ 2. Service Test (Business Logic)

import { createUser } from "../../src/services/user.service";

test("should create user", async () => {
  const user = await createUser({ email: "test@test.com", password: "123456" });
  expect(user.email).toBe("test@test.com");
});

✅ 3. API Test (Supertest)

📄 tests/api/auth.test.ts

import request from "supertest";
import app from "../../src/app";

describe("Auth API", () => {

  test("should register user", async () => {
    const res = await request(app)
      .post("/api/auth/register")
      .send({
        email: "test@test.com",
        password: "123456"
      });

    expect(res.status).toBe(201);
  });

});

✅ 4. Auth Test (JWT / Protected Route)

test("should access protected route", async () => {
  const login = await request(app)
    .post("/api/auth/login")
    .send({ email: "test@test.com", password: "123456" });

  const token = login.body.token;

  const res = await request(app)
    .get("/api/protected")
    .set("Authorization", `Bearer ${token}`);

  expect(res.status).toBe(200);
});

🧠 Testing Guidelines

✅ MUST TEST

  • Utility functions
  • Services (business logic)
  • Controllers
  • Authentication flows
  • Critical APIs
  • Bug fixes (regression tests)

⚠️ OPTIONAL

  • Simple DTOs
  • Non-critical helpers

❌ DO NOT TEST

  • Third-party libraries
  • Framework internals
  • Trivial code

🔐 Best Practices

  • Use test DB only (never production DB)
  • Reset DB after each test
  • Keep tests deterministic
  • Avoid shared state
  • Use mocks when needed

🚀 CI/CD Integration

📄 .github/workflows/test.yml

name: Run Tests

on:
  push:
    branches: [main]
  pull_request:

jobs:
  test:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
        with:
          node-version: 18

      - run: npm install
      - run: npm test

📊 Coverage Goal

  • Minimum coverage: 80%

  • Focus on:

    • services
    • controllers
    • critical flows

✅ Acceptance Criteria

  • Jest configured
  • Test DB working
  • Unit tests added
  • API tests added
  • Auth tested
  • Tests pass locally
  • CI pipeline runs tests
  • Coverage report generated

🔥 Priority

High — required before scaling backend features


🧠 Engineering Rule

  • No test → No merge ❌
  • Every bug → must include test ✅
  • Tests must run in CI before deploy

⚡ Final Outcome

After this issue:

  • Backend becomes testable, reliable, scalable
  • Safe refactoring enabled
  • Production bugs reduced significantly

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions