SuperTest: File Uploads, Test Isolation & Pitfalls
File Uploads
it('uploads a document', async () => {
// .attach() simulates a multipart/form-data upload -- no need for
// an actual browser or real client to construct the multipart body
await request(app)
.post('/api/upload')
.attach('document', 'test-fixtures/report.pdf')
.expect(200)
})Database State Isolation Between Tests
describe('POST /api/users', () => {
beforeEach(async () => {
// API calls made via SuperTest genuinely write to the underlying
// test database -- reset it before each test so one test's
// created data doesn't leak into the next test's assertions
await db.migrate.rollback()
await db.migrate.latest()
})
it('creates a user', async () => {
await request(app).post('/api/users').send({ name: 'Bob' }).expect(201)
})
})Avoid Brittle Exact-Match Assertions
// FRAGILE -- id and createdAt vary between test runs, so this
// exact-match assertion breaks unpredictably
.expect(200, { id: 1, name: 'Alice', createdAt: '2024-01-01T00:00:00Z' })
// BETTER -- assert only the stable, deterministic fields
.expect(200)
.expect((res) => {
if (res.body.name !== 'Alice') throw new Error('unexpected name')
})Integration Coverage Beyond a Unit-Tested Handler
Testing via SuperTest exercises the full request pipeline -- middleware (auth, body parsing, error handling), routing, and handler logic together -- catching bugs in how these pieces interact that a handler-only unit test (calling the function directly with mocked req/res objects) might miss, like an auth middleware applied to the wrong routes.
Testing GraphQL Endpoints
// A GraphQL server ultimately exposes a normal HTTP POST endpoint --
// from SuperTest's perspective, it's just another JSON-over-HTTP call
await request(app)
.post('/graphql')
.send({ query: '{ user(id: 1) { name } }' })
.expect(200)
.expect((res) => {
if (res.body.data.user.name !== 'Alice') throw new Error('mismatch')
})Keep your own version of these notes — editable, searchable, and organised by your stack.
Start free