From harness-claude
Tests Node.js APIs and modules using Supertest for HTTP endpoints, Nock for external mocks, testcontainers for DB integration, and temp dirs for FS ops.
npx claudepluginhub intense-visions/harness-engineering --plugin harness-claudeThis skill uses the workspace's default tool permissions.
> Test Node.js APIs and modules using supertest, nock, and test containers
Provides patterns for writing integration tests using real test databases, Docker containers with Testcontainers, Supertest for APIs, and Prisma transaction isolation.
Builds reliable Jest test suites for NestJS modules, services, and controllers covering unit, integration, and e2e tests. Use for TestModule setup, mocking providers, database fakes, and debugging flaky tests.
Provides Jest, Vitest, and Testing Library patterns for unit, integration, E2E testing in JavaScript/TypeScript apps, including mocking, fixtures, and TDD workflows.
Share bugs, ideas, or general feedback.
Test Node.js APIs and modules using supertest, nock, and test containers
import request from 'supertest';
import { app } from '../app';
describe('POST /api/users', () => {
it('creates a user and returns 201', async () => {
const response = await request(app)
.post('/api/users')
.send({ name: 'Alice', email: 'alice@test.com' })
.set('Authorization', `Bearer ${testToken}`)
.expect('Content-Type', /json/)
.expect(201);
expect(response.body).toMatchObject({
name: 'Alice',
email: 'alice@test.com',
});
expect(response.body.id).toBeDefined();
});
it('returns 400 for invalid input', async () => {
const response = await request(app).post('/api/users').send({ name: '' }).expect(400);
expect(response.body.errors).toBeDefined();
});
});
import nock from 'nock';
beforeEach(() => {
nock('https://api.stripe.com')
.post('/v1/charges')
.reply(200, { id: 'ch_test', status: 'succeeded' });
});
afterEach(() => {
nock.cleanAll();
});
it('processes payment through Stripe', async () => {
const result = await paymentService.charge({
amount: 1000,
currency: 'usd',
source: 'tok_visa',
});
expect(result.status).toBe('succeeded');
});
import { PostgreSqlContainer } from '@testcontainers/postgresql';
let container;
let prisma;
beforeAll(async () => {
container = await new PostgreSqlContainer().start();
process.env.DATABASE_URL = container.getConnectionUri();
// Run migrations
prisma = new PrismaClient();
await prisma.$executeRawUnsafe(migrationSQL);
}, 60_000);
afterAll(async () => {
await prisma.$disconnect();
await container.stop();
});
afterEach(async () => {
await prisma.user.deleteMany();
});
import { mkdtemp, rm } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
let tempDir: string;
beforeEach(async () => {
tempDir = await mkdtemp(join(tmpdir(), 'test-'));
});
afterEach(async () => {
await rm(tempDir, { recursive: true, force: true });
});
it('writes and reads config file', async () => {
const configPath = join(tempDir, 'config.json');
await writeConfig(configPath, { port: 3000 });
const config = await readConfig(configPath);
expect(config.port).toBe(3000);
});
// Service accepts dependencies as constructor params
class UserService {
constructor(
private db: Database,
private emailClient: EmailClient
) {}
async createUser(data: CreateUserInput) {
const user = await this.db.user.create(data);
await this.emailClient.sendWelcome(user.email);
return user;
}
}
// In tests: inject mocks
it('sends welcome email on user creation', async () => {
const mockEmail = { sendWelcome: vi.fn() };
const service = new UserService(testDb, mockEmail);
await service.createUser({ name: 'Alice', email: 'alice@test.com' });
expect(mockEmail.sendWelcome).toHaveBeenCalledWith('alice@test.com');
});
nock('https://api.example.com')
.get('/users')
.query({ role: 'admin' })
.matchHeader('Authorization', /Bearer .+/)
.reply(200, [{ id: '1', name: 'Admin' }]);
afterEach(() => {
if (!nock.isDone()) {
nock.cleanAll();
throw new Error('Not all nock interceptors were used');
}
});
Node.js testing involves testing HTTP APIs, external service integrations, file operations, and database queries. The strategy combines supertest (HTTP testing), nock (HTTP mocking), and test containers (database isolation).
Supertest: Creates an in-process HTTP connection to your Express/Fastify app without starting a real server. This is faster and more reliable than making real HTTP requests to a running server.
Nock: Intercepts HTTP requests made by http/https/fetch and returns configured responses. Unlike MSW, nock operates at the Node.js HTTP module level, not the network level.
Nock vs MSW: Nock patches Node.js HTTP internals; MSW uses a service worker (browser) or interceptor (Node). MSW is better for testing code that uses fetch; nock is better for code that uses http/https directly.
Trade-offs: