Skip to content

Testing Strategy — Practical

math.test.ts
import { describe, it, expect } from 'vitest';
import { add } from './math';
describe('add', () => {
it('returns sum of two ints', () => {
expect(add(2, 3)).toBe(5);
});
it.each([
[0, 0, 0],
[-1, 1, 0],
[1.5, 2.5, 4],
])('add(%d, %d) === %d', (a, b, want) => {
expect(add(a, b)).toBe(want);
});
});
it('publishes event after save', async () => {
const spy = vi.fn();
bus.on('UserCreated', spy);
await service.createUser({ email: 'a@b' });
await vi.waitFor(() => expect(spy).toHaveBeenCalled());
});
afterEach(() => {
vi.useRealTimers();
vi.restoreAllMocks();
});

Integration test with Testcontainers (Postgres)

Section titled “Integration test with Testcontainers (Postgres)”
import { PostgreSqlContainer, StartedPostgreSqlContainer } from '@testcontainers/postgresql';
import { Pool } from 'pg';
let pg: StartedPostgreSqlContainer;
let pool: Pool;
beforeAll(async () => {
pg = await new PostgreSqlContainer('postgres:16').start();
pool = new Pool({ connectionString: pg.getConnectionUri() });
await pool.query(`CREATE TABLE users(id SERIAL PRIMARY KEY, email TEXT UNIQUE)`);
});
afterAll(async () => { await pool.end(); await pg.stop(); });
it('inserts unique users', async () => {
await pool.query(`INSERT INTO users(email) VALUES ('a@b')`);
await expect(pool.query(`INSERT INTO users(email) VALUES ('a@b')`))
.rejects.toThrow(/duplicate/);
});
import { Pact } from '@pact-foundation/pact';
const provider = new Pact({ consumer: 'web', provider: 'users-api' });
beforeAll(() => provider.setup());
afterAll(() => provider.finalize());
it('GET /users/1 returns user', async () => {
await provider.addInteraction({
state: 'user 1 exists',
uponReceiving: 'a request for user 1',
withRequest: { method: 'GET', path: '/users/1' },
willRespondWith: { status: 200, body: { id: '1', email: 'a@b' } },
});
const r = await fetch(`${provider.mockService.baseUrl}/users/1`);
expect(await r.json()).toEqual({ id: '1', email: 'a@b' });
});
import fc from 'fast-check';
it('reverse twice = identity', () => {
fc.assert(fc.property(fc.array(fc.integer()), arr => {
expect(reverse(reverse(arr))).toEqual(arr);
}));
});
import request from 'supertest';
import { app } from '../app';
it('POST /users returns 201', async () => {
const r = await request(app).post('/users').send({ email: 'a@b.com' });
expect(r.status).toBe(201);
expect(r.body).toMatchObject({ email: 'a@b.com' });
});
import pytest
@pytest.mark.parametrize("a,b,want", [(0,0,0), (-1,1,0), (2,3,5)])
def test_add(a, b, want):
assert add(a, b) == want
@pytest.fixture
def db():
conn = create_test_db()
yield conn
conn.rollback()
conn.close()
def test_insert(db):
db.execute("INSERT INTO users(email) VALUES ('a@b')")
assert db.fetchone("SELECT count(*) FROM users")[0] == 1
import { setupServer } from 'msw/node';
import { http, HttpResponse } from 'msw';
const server = setupServer(
http.get('https://api.x.com/users/1', () => HttpResponse.json({ id: 1 }))
);
beforeAll(() => server.listen());
afterAll(() => server.close());
import http from 'k6/http';
import { check, sleep } from 'k6';
export const options = {
stages: [
{ duration: '30s', target: 50 },
{ duration: '2m', target: 200 },
{ duration: '30s', target: 0 },
],
thresholds: {
http_req_duration: ['p(95)<500', 'p(99)<1000'],
http_req_failed: ['rate<0.01'],
},
};
export default function () {
const r = http.get('https://api/items');
check(r, { 'status 200': (x) => x.status === 200 });
sleep(1);
}
import { test, expect } from '@playwright/test';
test('user can sign up and see dashboard', async ({ page }) => {
await page.goto('/signup');
await page.fill('[name=email]', 'a@b.com');
await page.fill('[name=password]', 'pw');
await page.click('text=Sign up');
await expect(page).toHaveURL('/dashboard');
});
jobs:
ci:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16
env: { POSTGRES_PASSWORD: pw }
ports: [ '5432:5432' ]
options: --health-cmd "pg_isready -U postgres" --health-interval 10s
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 20, cache: pnpm }
- run: pnpm i --frozen-lockfile
- run: pnpm lint
- run: pnpm test:unit
- run: pnpm test:integration
- run: pnpm build
  • Node: Vitest / Jest / Mocha + chai. Supertest. Testcontainers. msw. fast-check. Pact.
  • Python: pytest + pytest-asyncio + pytest-postgresql. responses/respx. hypothesis.
  • Go: stdlib testing + testify. Testcontainers-go. gomock. quick (PBT).
  • Java/Kotlin: JUnit5 + AssertJ + Mockito. Testcontainers. RestAssured. Pact-JVM.
  • Load: k6, Locust, wrk, vegeta.
  • E2E: Playwright, Cypress.
  • Mutation: Stryker (JS), mutmut (Python), PIT (Java).