Practice Lab Guide

Project 12: Automated Test Suite for the IoT Backend

Configure a Jest and Supertest integration environment to validate API route handlers and validation middlewares.
Domain
Automated Integration Testing
Difficulty
⭐⭐⭐⭐☆ (Advanced)
Course Module
Testing & Validation (Node.js/TS)
Deliverables
Jest Config File, Integration Test Suite
1. Test Suite Execution Lifecycle

To verify API endpoints reliably, we use automated integration tests instead of manual curl commands. The diagram below details the test execution lifecycle. The Jest harness launches a mock Express server, compiles the TypeScript files in memory using `ts-jest`, executes test cases sequentially, uses Supertest to query routes, asserts HTTP status responses, and shuts down the server.

JEST ENGINE HARNESS ts-jest compiler Compiles TS in-memory Supertest Client Injects HTTP Requests jest.config.ts Locates *.test.ts files RUNNER LIFECYCLE beforeAll() Starts mock database it('should assert...') Executes route test cases afterAll() Closes server sockets TARGET API HANDLERS GET /api/devices Asserts: Array length > 0 POST /api/telemetry Asserts: 201 Created Pass Status: OK All assertions successful
2. Part 1: Step-by-Step Individual Test Configurations & Commands

Follow these detailed steps to install the test dependencies, configure Jest, and run the test suite inside your VM guest.

STEP 1

Install Jest, ts-jest, and Supertest Dependencies

Open a terminal window inside the Ubuntu VM. Navigate to the project directory and install the testing packages as devDependencies.

ubuntu@iot-vm:~$ cd ~/workspace/backend_scaffold && npm install -D jest @types/jest ts-jest supertest @types/supertest
We run `npm install -D` to download Jest (test runner), Supertest (HTTP assertions), and `ts-jest` to compile TypeScript test files in memory.
STEP 2

Generate Jest Configuration File

Create a Jest configuration file to define how tests are executed and compiled.

ubuntu@iot-vm:~/workspace/backend_scaffold$ npx ts-jest config:init
We run `ts-jest config:init` to generate a default configuration file (`jest.config.js`) that configures Jest to use the `ts-jest` preprocessor.
STEP 3

Create Directory for Test Files

Create a dedicated directory to organize your test files.

ubuntu@iot-vm:~/workspace/backend_scaffold$ mkdir -p src/tests
We create the `src/tests` directory to isolate our automated test files from the application source code.
STEP 4

Create the API Route Integration Test File

Open the graphical text editor in the background to write the test cases.

ubuntu@iot-vm:~/workspace/backend_scaffold$ gedit src/tests/api.test.ts &
We open `api.test.ts` in `gedit` using the `&` operator to run the text editor in the background, keeping the terminal prompt active.
STEP 5

Register Test Scripts in package.json

Edit `package.json` to configure the test scripts and code coverage options.

Settings to update (gedit package.json): 1. Locate the "scripts" block. 2. Replace '"test": "echo \"Error: no test specified\" && exit 1"' with: '"test": "jest --runInBand --detectOpenHandles --forceExit"' 3. Add a coverage script: '"coverage": "jest --coverage"' 4. Save and exit the editor.
We add the test scripts to `package.json`, passing the `--runInBand` and `--detectOpenHandles` flags to ensure tests run sequentially and open connections are closed.
STEP 6

Execute the Automated Test Suite

Run the test suite in the VM terminal to verify the API endpoints and validation middleware.

ubuntu@iot-vm:~/workspace/backend_scaffold$ npm run test
We run `npm run test` to execute the Jest test runner, validating that all HTTP assertions and schema validation checks pass.
STEP 7

Generate Code Coverage Reports

Run the coverage script in the terminal to measure code coverage.

ubuntu@iot-vm:~/workspace/backend_scaffold$ npm run coverage
We run `npm run coverage` to generate a coverage report, analyzing what percentage of route handlers and validation statements are covered by tests.
3. Mock Jest Code Coverage Console Dashboard Representation

The diagram below represents the Jest code coverage report, displaying the statement, branch, function, and line coverage metrics for the backend API.

PASS src/tests/api.test.ts File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s ----------------|---------|----------|---------|---------|------------------- All files | 95.24 | 87.50 | 100 | 95.24 | src/ | | | | | index.ts | 100 | 100 | 100 | 100 | src/routes/ | | | | | api.ts | 92.31 | 75 | 100 | 92.31 | 18-20 Test Suites: 1 passed, 1 total Tests: 3 passed, 3 total
4. Part 2: Complete Integration Test Suite Code

Copy the integration test file code below and save it to your project folder to complete the test suite configuration.

Asset 1: Integration Test Suite File (`src/tests/api.test.ts`)

Line-by-Line Code Breakdown

// Setup Project 12: Automated Integration Test Suite import request from 'supertest'; import express from 'express'; import apiRouter from '../routes/api'; const app = express(); app.use(express.json()); app.use('/api', apiRouter); describe('IoT Backend API Integration Tests', () => { // Test Case 1: GET /api/devices it('should retrieve a list of registered devices with status 200 OK', async () => { const res = await request(app) .get('/api/devices') .set('Accept', 'application/json'); expect(res.status).toBe(200); expect(Array.isArray(res.body)).toBe(true); expect(res.body[0].id).toBe('node_1'); }); // Test Case 2: POST /api/telemetry (Success path) it('should accept valid telemetry readings with status 201 Created', async () => { const validPayload = { deviceId: 'node_1', temperature: 24.5, light: 500 }; const res = await request(app) .post('/api/telemetry') .send(validPayload) .set('Content-Type', 'application/json'); expect(res.status).toBe(201); expect(res.body.message).toBe('Telemetry saved successfully'); expect(res.body.reading.temperature).toBe(24.5); }); // Test Case 3: POST /api/telemetry (Validation Failure path) it('should reject invalid telemetry values with status 400 Bad Request', async () => { const invalidPayload = { deviceId: 'node_1', temperature: 'very_hot', // Should fail validation (expects float number) light: 500 }; const res = await request(app) .post('/api/telemetry') .send(invalidPayload) .set('Content-Type', 'application/json'); expect(res.status).toBe(400); expect(res.body.error).toBeDefined(); }); });
5. Deliverables Summary

Created Workspace Assets

  • TypeScript test file: src/tests/api.test.ts.
  • Jest runtime configuration parameters: jest.config.js.
  • Modified package configuration scripts: package.json.

Verification Proof

  • Console logs showing 3 tests passing successfully in the terminal.
  • A coverage report detailing statement and branch coverage percentages.
6. Closing Explanation: Why We Did This & What It Accomplishes

Architectural Intent & Operational Impact

Why We Did This

What This Accomplishes