Master

Automated testing

A codebase you cannot test is a codebase you cannot trust. Learn to write tests for your routes and functions with a real test runner, and run them automatically.

What you will see

Tests run: 3 Skipped obvious tests: True

About 2 min of reading

Quick check

Before this new lesson, one small question about the lesson you just finished. Recall it, do not look it up.

Try It

Test in order of value

1

Score test stages by value.

def test_value(stage): return {"business logic": 3, "routes": 2, "database queries": 1, "obvious router": 0}[stage] print() print("Automated testing") stages = ["business logic", "routes", "database queries", "obvious router"] for s in stages: print("%-18s value=%d" % (s, test_value(s)))

Before you run it, say out loud what it will print. Then open the answer.

What it prints
Automated testing business logic value=3 routes value=2 database queries value=1 obvious router value=0
2

Run a focused suite and skip the obvious.

tests = { "pricing logic": True, "route returns 200": True, "bad SQL corrupts": True, "router forwards": False, } print("Tests run: %d" % sum(1 for v in tests.values() if v)) print("Skipped obvious tests: True")

Before you run it, say out loud what it will print. Then open the answer.

What it prints
Tests run: 3 Skipped obvious tests: True

Put it in order

Before you write anything, put these lines back in the right order. Click a line to move it up one step. This support step is easier than writing code from scratch, and it is here to make the last example stick.

Data Flow

A test fires a request at the app, the handler runs, and the test asserts the status and body match expectations.
1 Jest loads the test file
↓
2 supertest sends a request to the app
↓
3 The handler executes in memory
↓
4 The response is captured
↓
5 expect() compares it to the assertion
↓
6 Jest records pass or fail
Why this works
What to test first

You cannot test everything. Test in this order of value:

  • **Business logic*: the functions that decide your app's behaviour (pricing, permissions, validation).
  • **Routes*: that each endpoint returns what it claims.
  • **Database queries*: the most error-prone part, since bad SQL corrupts data.

Skip testing the obvious, like a router that just forwards a request. A focused suite around the risky parts gives you confidence to change code without fear.

Examples

A first test

Use Jest to describe how your code should behave:

const request = require('supertest');
const app = require('../server');

test('GET / returns welcome', async () => {
  const res = await request(app).get('/');
  expect(res.statusCode).toBe(200);
  expect(res.text).toContain('welcome');
});

supertest lets you send real HTTP requests to your app in memory, no server needs to be running. The test asserts the status and the body, and Jest reports whether each one passed.

Running a suite

Add a test script to package.json:

{
  "scripts": {
    "test": "jest"
  }
}

Jest looks for files named *.test.js (or in a __tests__ folder) and runs every test(...) inside them. A suite can have dozens of cases, each isolated, each telling you if a part of your app still works after you change it. This is how large projects stay reliable.

Words this lesson uses

Each one is defined here on this site. Open the entry for the full definition and the other words it points at.

← Building a REST API
Previous Lesson
Building a REST API
Next Lesson
Deploying your app
Deploying your app →