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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions