autohand "Write integration tests for the /api/users endpoints. Test create, read, update, delete operations against a test database. Use Supertest with Jest. Include setup and teardown."

What you'll learn

  • How to set up a test database and Jest global setup/teardown for integration tests
  • How to prompt Autohand to generate Supertest-based integration tests for CRUD endpoints
  • How to add authentication tests with token generation helpers
  • How to ensure test isolation so tests pass reliably in any order

Before you start

  • Autohand Code installed
  • Jest and Supertest installed (npm install --save-dev jest supertest)
  • A separate test database with a TEST_DATABASE_URL environment variable
  • Your Express app exported from its entry file without calling app.listen()

Set up the test database

Point your test runner at a dedicated test database by adding a setup file. Ask the agent to create it.

bash

autohand "Create a Jest global setup file at tests/setup.js that connects to the test database, runs migrations, and disconnects after all tests complete."

The agent reads your database client (Knex, Prisma, Sequelize, or whatever your project uses) and generates the appropriate setup. Then wire it into your Jest config.

json

{
  "jest": {
    "globalSetup": "./tests/setup.js",
    "globalTeardown": "./tests/teardown.js",
    "testEnvironment": "node"
  }
}

With the setup in place, each test file can rely on a clean, migrated database at the start of the run.

Write the test prompt

Point the agent at your routes file and describe the operations you want covered. The more specific you are, the more complete the output.

bash

autohand "Write integration tests for the /api/users endpoints. Test create, read, update, delete operations against a test database. Use Supertest with Jest. Include setup and teardown."

The agent reads your route file to understand the endpoint shapes before writing a single line of tests. Here is a typical route file it might read.

javascript

// routes/users.js
const router = require('express').Router();
const db = require('../db');

router.get('/', async (req, res) => {
  const users = await db('users').select('*');
  res.json(users);
});

router.post('/', async (req, res) => {
  const [id] = await db('users').insert(req.body);
  const user = await db('users').where({ id }).first();
  res.status(201).json(user);
});

router.put('/:id', async (req, res) => {
  await db('users').where({ id: req.params.id }).update(req.body);
  const user = await db('users').where({ id: req.params.id }).first();
  res.json(user);
});

router.delete('/:id', async (req, res) => {
  await db('users').where({ id: req.params.id }).del();
  res.status(204).send();
});

module.exports = router;
Notice that beforeEach clears the table before every test. This is test isolation. Without it, tests can affect each other in ways that are very hard to debug.

Run the integration tests

Run the tests with your test database URL set as an environment variable.

bash

TEST_DATABASE_URL=postgres://localhost/myapp_test npm test

Or add it to a .env.test file and load it with dotenv.

# .env.test
TEST_DATABASE_URL=postgres://localhost/myapp_test
NODE_ENV=test npm test

If a test fails on a 400 that you are not returning yet, that is a gap in your implementation, not the test. The test found a missing validation. Ask the agent to add it.

autohand "The POST /api/users test expects a 400 when required fields are missing but the route currently returns 500. Add input validation to the create user route."

Add authentication tests

If your API uses JWT or session-based authentication, ask the agent to add protected route tests. Give it the auth structure so it generates working tokens.

autohand "Add tests for the authentication-protected routes. Generate a valid JWT for the test user using the same secret in process.env.JWT_SECRET. Test that requests without a token return 401."

The agent adds a helper at the top of the test file that mints a token for tests.

const jwt = require('jsonwebtoken');

function makeToken(userId) {
  return jwt.sign({ userId }, process.env.JWT_SECRET, { expiresIn: '1h' });
}

describe('GET /api/users (protected)', () => {
  it('returns 401 when no Authorization header is sent', async () => {
    const res = await request(app).get('/api/users');
    expect(res.status).toBe(401);
  });

  it('returns 200 with a valid token', async () => {
    const token = makeToken(1);
    const res = await request(app)
      .get('/api/users')
      .set('Authorization', `Bearer ${token}`);
    expect(res.status).toBe(200);
  });
});

Handle test isolation

Integration tests that share state produce flaky results. If you see tests that pass in isolation but fail when run together, ask the agent to audit isolation.

autohand "Review tests/users.integration.test.js for test isolation issues. Each test should start with a clean database state and not depend on data created by a previous test."

Common isolation problems the agent looks for include tests that insert rows without cleaning up, tests that assume a specific auto-increment ID value, and tests that run in a specific order to pass.

Tip: Run your tests in random order periodically to catch hidden ordering dependencies. Add "--randomize" to your Jest command: jest --randomize. Flaky tests in random order are isolation problems waiting to cause CI failures.

What you learned

  • You set up a test database with Jest global setup and teardown
  • You generated integration tests covering CRUD operations with Supertest
  • You added authentication tests with a JWT token helper
  • You audited test isolation to prevent flaky results from shared state
Try next autohand "These tests are failing. Read the test output, identify the root cause, and fix the code. Do not modify the tests unless they have a bug."