Testing: inject and node:test
A test is code that calls your API and checks the reply. Fastify can run a request right in memory with app.inject(): no port is taken, no network is needed, and the test is fast. Tests are run by the node:test module built into Node.js, so there is nothing to install.
The run script
Add a script to package.json:
{
"scripts": {
"test": "node --test"
}
}node --test finds and runs files with names like *.test.js.
The first test
This is where separating app.js and server.js pays off: the test creates the application with buildApp without calling listen. Create test/posts.test.js:
// test/posts.test.js
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { buildApp } from '../src/app.js';
process.env.DB_FILE = ':memory:';
process.env.JWT_SECRET = 'test-secret-1234567890';
process.env.ADMIN_NAME = 'anna';
process.env.ADMIN_PASSWORD = 'test-password';
test('GET /posts returns an empty list', async (t) => {
const app = buildApp();
t.after(() => app.close());
const response = await app.inject({ method: 'GET', url: '/posts' });
assert.equal(response.statusCode, 200);
assert.deepEqual(response.json(), []);
});DB_FILE=':memory:' creates an empty in-memory database for the test, so real posts are not affected. t.after() closes the application after the test, and response.json() parses the reply body.
Run the tests:
npm testThe terminal shows the test name marked as passed and the summary pass 1.
A request with a body
You can pass a method, address, headers and a body to inject — Fastify turns an object into JSON:
// test/posts.test.js
test('POST /posts without a token returns 401', async (t) => {
const app = buildApp();
t.after(() => app.close());
const response = await app.inject({
method: 'POST',
url: '/posts',
payload: { title: 'Test', body: 'Text' },
});
assert.equal(response.statusCode, 401);
});The test confirms that the protection from the authentication lesson works.
What the reply has
| Property | What it holds |
|---|---|
response.statusCode | Status code |
response.headers | Reply headers |
response.body | The body as a string |
response.json() | The body parsed from JSON |
Common mistake
If you do not close the application after a test, the open database and other plugin resources can keep the process alive, and npm test hangs after printing the results. The onClose hooks do not run either. Add t.after(() => app.close()) to every test that creates an application.