INTRO
Unit tests for pure functions are cheap. API integration tests cost more setup but catch the bugs unit tests miss: wrong middleware order, bad status codes, and schema drift between validation and persistence. I invest in a thin harness that boots the app against a disposable database.
Test app factory
Export `createApp()` that wires the same middleware as production but swaps config: in-memory Mongo, test secrets, disabled external webhooks.
Avoid starting a new server per test when Supertest can call `app` directly unless you need full HTTP socket behavior.
code-snippet.typescript
const res = await request(app)
.post('/api/orders')
.set('Authorization', `Bearer ${token}`)
.send({ sku: 'ABC', qty: 2 })
expect(res.status).toBe(201)
expect(res.body).toMatchObject({ status: 'pending' })Data seeding and isolation
Truncate or drop collections between tests or use transactions if your test runner supports rollback per suite. Parallel tests need separate DB names per worker (`jest --runInBand` is simpler early).
Factory functions build valid users/orders instead of copy-pasting JSON blobs.
What to mock and what not to
Mock third-party HTTP (payment, email) at the client boundary; assert you sent the right payload.
Do not mock your own database if the goal is integration confidence - use a real test instance.
KEY TAKEAWAYS
“Boot the real middleware stack in tests.”
- 02
Use a dedicated test database and deterministic seeds.
- 03
Mock external SaaS; keep Mongo/Postgres real for integration suites.
CONCLUSION
Closing thought
Supertest pays off when tests resemble production requests. Invest once in app factory + DB hygiene, then add cases as you fix incidents. Ready to talk scope? Use Contact — or browse Services for fit.
CONTINUE
Related pages
UP NEXT
A Practical GitHub Actions CI Pipeline for Next.js
Minimal workflows: cache dependencies, run eslint and tsc, run unit tests, build Next. When to add Playwright and how to keep secrets out of forks.
Read next article