Skip to main content
A presentation slide titled "Test Coverage & Quality Analysis" with a large dark curved shape on the right containing the word "Demo." The small copyright "KodeKloud" appears in the lower-left corner.
This article presents a testing audit of an Express.js “login demo” application. The goal is to evaluate test coverage, testing quality and patterns, identify missing tests, and produce a prioritized remediation plan with small, concrete code fixes and configuration changes you can apply immediately. Included below:
  • Representative CLI interactions captured from the audit agent used to run the analysis
  • The condensed audit prompt given to the agent
  • Commands the agent used to inspect the project and sample output
  • A structured findings report with prioritized remediations
  • A phased test improvement plan and minimal example code/config
CLI interaction that launched the code-review agent:
# Launching the code-review agent in the project directory
jeremy@MACSTUDIO Express-login-demo % claude

* Welcome to Claude Code!

/help for help, /status for your current setup

cwd: /Users/jeremy/Repos/Claude Code Course/Express-login-demo

> Try "fix typecheck errors"

? for shortcuts
Audit prompt sent to the review agent (condensed):
Evaluate testing implementation for this software project

Analyze:
1. TEST COVERAGE
  - Unit test coverage percentage
  - Integration test presence
  - E2E test coverage
  - Uncovered critical paths

2. TEST QUALITY
  - Test naming clarity
  - Arrange-Act-Assert pattern
  - Test independence
  - Mock usage appropriateness
  - Test data management

3. TEST PATTERNS
  - Test pyramid adherence (unit > integration > E2E)
  - Testing anti-patterns (testing implementation vs behavior)
  - Brittle tests identification
  - Test speed issues

4. MISSING TESTS
  - Error scenarios
  - Edge cases
  - Security tests
  - Performance tests

Provide:
- Structured findings report
- Importance scale (1-10)
- Remediation: precise code-level fix or config change (snippets welcome)
- A test improvement plan with examples
Representative project inspection commands (used by the agent) and example outputs:
# Find tests in project (example)
cd "/Users/jeremy/Repos/Claude Code Course/Express-login-demo" && \
find . -name "*.test.*" -o -name "*.spec.*" -not -path "./node_modules/*" | head -10
# Output (example):
# ./node_modules/pstree.remy/tests/index.test.js
# ./node_modules/pg-protocol/dist/inbound-parser.test.js.map
# ...
# Find test directories
cd "/Users/jeremy/Repos/Claude Code Course/Express-login-demo" && \
find . -name "test" -o -name "tests" -type d -not -path "./node_modules/*" | head -10
# Output (example):
# ./node_modules/es-errors/test
# ./node_modules/math-intrinsic/test
# ...
Key files surfaced by the scan (partial):
  • server.js — application entry (read 19 lines)
  • routes/auth.js — authentication logic (read 98 lines)
  • config/database.js — DB configuration (read 19 lines)
  • package.json — project metadata
This audit found no application-level test suite in the project. The immediate priority is to add a test framework and basic unit/integration tests around authentication and database integration.

Executive Summary

  • The Express login demo currently has no application tests (unit/integration/e2e) exercising authentication and database paths.
  • Critical authentication flows, input validation, and security behaviors remain untested.
  • Overall Test Maturity Score: 0/10

1. TEST COVERAGE ANALYSIS

Unit Test Coverage

  • Coverage: 0% (no unit tests observed).
  • High-priority files with zero coverage:
    • routes/auth.js — HIGH CRITICALITY
    • config/database.js — HIGH CRITICALITY
    • server.js — bootstrap and app wiring
  • Importance: 10/10

Integration Test Coverage

  • None found.
  • Missing tests for:
    • DB connection and health checks
    • Auth routes (register, login, token generation/validation)
    • Middleware (authentication and request validation)

End-to-End (E2E)

  • None found.
  • Missing full request flows verifying user creation → login → token usage → token expiry/errors.

2. TEST QUALITY (Checklist for when tests are added)

  • Clear, intent-revealing test names.
  • Arrange–Act–Assert pattern used consistently.
  • Tests are independent and isolate side effects.
  • Proper mocking/stubbing of external resources (DB, external APIs).
  • Deterministic test data and reliable teardown/setup.

3. TEST PATTERNS & ANTI-PATTERNS

  • Follow the Test Pyramid: unit tests >> integration tests >> E2E tests.
  • Favor behavior-based testing over implementation details.
  • Identify brittle tests (timers, order-dependent DB state, flaky network).
  • Keep tests fast; isolate longer-running tests.

4. MISSING TESTS (examples)

  • Input validation: empty payloads, malformed JSON, missing fields.
  • Security: SQL injection attempts, JWT tampering, token expiry behavior.
  • Error handling: DB failures, third-party outages.
  • Performance: concurrent login attempts, token generation throughput.

Below is a condensed table of the highest-priority findings and suggested remediations.
FindingImportanceRemediation
No test framework or scripts10Add Jest + Supertest, update package.json scripts (see snippet). Install via npm install --save-dev jest supertest.
App not exported for testability9Update server.js to export app and only call app.listen when run directly.
No unit tests for auth logic10Extract pure auth logic into testable modules and add Jest unit tests.
No integration tests for auth routes10Use Supertest against exported app. Add tests for error and success cases; use test DB or mocks.
DB interactions not covered9Mock DB calls in unit tests; use ephemeral/test DB for integration tests.
Security tests missing10Add tests for SQL injection, invalid/tampered tokens, token expiry.
No performance tests6Add load tests later (Artillery or k6) for concurrent login scenarios.
Detailed remediations (examples)
  1. Add test scripts and dev dependencies
  • package.json snippet:
{
  "scripts": {
    "test": "jest",
    "test:watch": "jest --watch",
    "test:coverage": "jest --coverage"
  },
  "devDependencies": {
    "jest": "^29.0.0",
    "supertest": "^6.3.0"
  }
}
Install:
npm install --save-dev jest supertest
  1. Export Express app for testability
  • Modify server.js so tests can import the app without starting the network listener:
// server.js
const express = require('express');
const app = express();

// ...app configuration, routes, middleware...

if (require.main === module) {
  // Only start the server when run directly
  const PORT = process.env.PORT || 3000;
  app.listen(PORT, () => console.log(`Server listening on ${PORT}`));
}

module.exports = app;
This pattern allows Supertest to use the app instance directly.
  1. Unit test skeleton for auth logic
// tests/unit/auth.test.js
const auth = require('../../routes/auth'); // adjust path if necessary

describe('auth module (unit)', () => {
  test('should expose expected functions', () => {
    expect(typeof auth).toBe('object'); // adjust to actual exports
  });
});
If routes/auth.js includes route wiring and business logic together, extract pure functions (validateLogin, hashPassword, verifyPassword) into separate modules for easier unit testing.
  1. Integration tests with Supertest
// tests/integration/auth-routes.test.js
const request = require('supertest');
const app = require('../../server'); // ensure server.js exports app

describe('Auth routes (integration)', () => {
  test('POST /register returns 400 on empty body', async () => {
    const res = await request(app).post('/register').send({});
    expect(res.status).toBe(400);
  });

  test('POST /login returns 401 for invalid credentials', async () => {
    const res = await request(app).post('/login')
      .send({ username: 'nope', password: 'wrong' });
    expect(res.status).toBe(401);
  });

  // Add success path (requires test DB fixture or mocking)
});
  1. Mocking DB interactions (Jest example)
// tests/unit/database.test.js
jest.mock('../../config/database', () => {
  return {
    query: jest.fn().mockResolvedValue({ rows: [] })
  };
});

const db = require('../../config/database');

test('db.query mocked', async () => {
  const res = await db.query('SELECT 1');
  expect(res.rows).toEqual([]);
  expect(db.query).toHaveBeenCalled();
});
If the project uses node-postgres, prefer parameterized queries to avoid SQL injection:
// Example DB helper using node-postgres
const { Pool } = require('pg');
const pool = new Pool();

async function getUserByUsername(username) {
  const res = await pool.query('SELECT * FROM users WHERE username = $1', [username]);
  return res.rows[0];
}
  1. Security test examples (SQL injection / token expiry)
test('login resists SQL injection attempts', async () => {
  const res = await request(app).post('/login').send({
    username: "' OR '1'='1",
    password: 'irrelevant'
  });
  // Expect authentication to fail, not return a token
  expect(res.status).toBe(401);
});
Add tests that assert tokens are rejected after expiry (configure short expiry in test environment or mock the verification).
Phase 1 — Foundation (Critical, target: 1 week)
  • Install Jest + Supertest and add npm scripts.
  • Export Express app from server.js.
  • Add repository test structure:
tests/
├── unit/
│   ├── auth.test.js
│   └── database.test.js
├── integration/
│   └── auth-routes.test.js
└── setup/
    └── test-db.js
  • Add minimal tests:
    • Unit tests for pure logic (password hashing, input validation).
    • Integration tests for auth routes validating error and success paths (using test DB or mocks).
Phase 2 — Defensive & Security Tests (High, target: 2 weeks)
  • Add security tests: SQL injection, JWT tampering, missing/invalid tokens, token expiry.
  • Add input validation and edge-case tests.
  • Add tests for timing/side-channel behaviors where appropriate (avoid username enumeration).
Phase 3 — Stability & Performance (Medium)
  • Add E2E tests that exercise full flows (register → login → access protected route).
  • Add load testing (Artillery or k6) for concurrent logins and token generation.
  • Add monitoring for test flakiness; refactor tests to be deterministic.

jest.config.js (basic)
module.exports = {
  testEnvironment: 'node',
  collectCoverageFrom: [
    "routes/**/*.js",
    "config/**/*.js",
    "lib/**/*.js"
  ],
  coverageDirectory: "coverage",
  testPathIgnorePatterns: [
    "/node_modules/"
  ]
};
Bootstrap test directories and example files:
mkdir -p tests/unit tests/integration tests/setup
# Create example test files as shown above

  1. Make app testable — export app from server.js (see snippet above).
  2. Simple Supertest integration example:
const request = require('supertest');
const app = require('../../server');

describe('Auth routes', () => {
  test('POST /register should return 400 for empty body', async () => {
    const res = await request(app).post('/register').send({});
    expect(res.status).toBe(400);
  });

  test('POST /login should return 401 for invalid credentials', async () => {
    const res = await request(app).post('/login')
      .send({ username: 'nosuchuser', password: 'bad' });
    expect(res.status).toBe(401);
  });
});
  1. Mock DB example (unit-level) — tests/unit/database.test.js:
jest.mock('../../config/database', () => ({
  query: jest.fn().mockResolvedValue({ rows: [] })
}));

const db = require('../../config/database');

test('mocked db query resolves', async () => {
  const res = await db.query('SELECT 1');
  expect(res.rows).toEqual([]);
  expect(db.query).toHaveBeenCalled();
});

  1. CRITICAL: Install Jest and Supertest; add test scripts. (1 day)
  2. CRITICAL: Export Express app from server.js to support tests. (1 day)
  3. CRITICAL: Add unit tests for authentication logic (password hashing, validation). (2–3 days)
  4. CRITICAL: Add integration tests for auth routes; mock DB or use test DB. (3–4 days)
  5. HIGH: Add security tests for SQL injection, JWT validation and expiry checks. (1 week)
  6. MEDIUM: Add performance/load tests (k6 or Artillery) for concurrent login behavior. (2+ weeks)
Risk: Running an unaudited authentication service in production is high-risk. Prioritize Phase 1 tasks immediately to enable safe development and refactoring.

  • Current Risk Level: EXTREME — authentication code is high-risk to operate without tests.
  • Consequences: security vulnerabilities, undetected regressions, unsafe refactors.
  • Recommended timeline: Phase 1 within 1 week, Phase 2 within 2–3 weeks.


  • Do not invent files or functions not present in the repository. When exact file/function names were not verifiable, remediation uses minimal, drop-in changes (export app, test common endpoints /register and /login). If file/function names differ, update tests to reflect the actual project layout.
  • If you prefer not to run a real DB during tests, use Jest mocks for config/database.js or set up a disposable test DB (Docker, test container, or an in-memory DB).
Next steps: implement Phase 1 (install test tooling, export app, add basic unit/integration tests), run the initial test suite, and iterate with security-focused tests.

Watch Video

Practice Lab