name: http-mocking description: HTTP mocking with nock - the only allowed mocking library
Nock HTTP Mocking Patterns
Critical Rule: ONLY nock for HTTP Mocking
This is a CRITICAL rule that MUST be enforced:
✅ ONLY nock is allowed for HTTP mocking
❌ NEVER use jest.mock, sinon, or fetch-mock
See failure-conditions skill Rule 4 for enforcement.
Core Principles
- Mock at HTTP level - Mock the network request, not internal methods
- Match requests accurately - Include method, path, headers, query params
- Return realistic responses - Use response formats matching real API
- Clean up after tests - Use
afterEach(() => nock.cleanAll()) - Verify mocks called - Assert
expect(nock.isDone()).toBe(true)
Basic HTTP Mock Pattern
import nock from 'nock';
describe('WebhookProducer', () => {
afterEach(() => {
nock.cleanAll(); // Clean up after each test
});
it('should list webhooks successfully', async () => {
// Mock the HTTP request
nock('https://api.github.com')
.get('/repos/octocat/Hello-World/hooks')
.reply(200, [
{
id: '12345678',
name: 'web',
active: true,
events: ['push', 'pull_request'],
config: {
url: 'https://example.com/webhook',
content_type: 'json'
},
created_at: '2024-01-01T00:00:00Z'
}
]);
// Execute the test
const producer = new WebhookProducer(httpClient);
const webhooks = await producer.list('octocat', 'Hello-World');
// Assertions
expect(webhooks).toHaveLength(1);
expect(webhooks[0].id).toBeDefined();
expect(nock.isDone()).toBe(true); // Verify mock was called
});
});
Mock with Request Headers
nock('https://api.github.com', {
reqheaders: {
'Authorization': 'Bearer test-token',
'Accept': 'application/vnd.github.v3+json'
}
})
.get('/repos/octocat/Hello-World/hooks')
.reply(200, mockData);
Mock Error Responses
it('should handle 404 error', async () => {
nock('https://api.github.com')
.get('/repos/octocat/Hello-World/hooks/999')
.reply(404, {
message: 'Not Found',
documentation_url: 'https://docs.github.com/rest'
});
// Test error handling
await expect(
producer.get('octocat', 'Hello-World', '999')
).rejects.toThrow(ResourceNotFoundError);
expect(nock.isDone()).toBe(true);
});
Mock POST Request
it('should create webhook', async () => {
const requestBody = {
name: 'web',
config: {
url: 'https://example.com/webhook',
content_type: 'json'
},
events: ['push'],
active: true
};
nock('https://api.github.com')
.post('/repos/octocat/Hello-World/hooks', requestBody)
.reply(201, {
id: '12345678',
...requestBody,
created_at: '2024-01-01T00:00:00Z'
});
const webhook = await producer.create('octocat', 'Hello-World', config);
expect(webhook.id).toBeDefined();
expect(nock.isDone()).toBe(true);
});
Mock with Query Parameters
nock('https://api.github.com')
.get('/repos/octocat/Hello-World/hooks')
.query({ per_page: 20, page: 1 })
.reply(200, mockData);
Reusable Mock Fixtures
// test/fixtures/webhookMocks.ts
export const mockWebhookResponse = {
id: '12345678',
name: 'web',
active: true,
events: ['push', 'pull_request'],
config: {
url: 'https://example.com/webhook',
content_type: 'json'
},
created_at: '2024-01-01T00:00:00Z',
updated_at: '2024-01-01T00:00:00Z'
};
export function mockListWebhooks(owner: string, repo: string, data: any[]) {
return nock('https://api.github.com')
.get(`/repos/${owner}/${repo}/hooks`)
.reply(200, data);
}
Validation Checklist
# Verify ONLY nock is used
grep "from ['\"]nock['\"]" test/unit/*.ts
# Should show nock imports
# Ensure NO forbidden mocking libraries
grep -E "jest\.mock|sinon|fetch-mock" test/*.ts
# Should return nothing (exit code 1)
# Verify mock cleanup present
grep "nock.cleanAll()" test/unit/*.ts
# Should show in afterEach blocks
# Verify mock verification present
grep "nock.isDone()" test/unit/*.ts
# Should show in test assertions
Anti-Patterns: What NOT to Do
❌ WRONG: Using jest.mock
// ❌ NO! Don't mock at method level
jest.mock('./WebhookProducer');
Why wrong: Mocks internal implementation instead of HTTP layer.
❌ WRONG: Using sinon
// ❌ NO! Don't use sinon
import sinon from 'sinon';
const stub = sinon.stub(producer, 'list');
Why wrong: Not allowed - violates testing rules.
❌ WRONG: Using fetch-mock
// ❌ NO! Use nock instead
import fetchMock from 'fetch-mock';
Why wrong: Not allowed - only nock is permitted.
✅ CORRECT: Using nock
// ✅ YES! Mock at HTTP level with nock
import nock from 'nock';
nock('https://api.github.com')
.get('/repos/octocat/Hello-World/hooks')
.reply(200, mockData);
Why correct: Mocks at HTTP level using approved library.
Standard Output Format
When documenting HTTP mocking strategy:
# HTTP Mocking Strategy: {ProducerName}
## Mock Library
✅ **nock** (ONLY allowed library)
## Mock Patterns Created
### List Operation
```typescript
nock('https://api.github.com')
.get('/repos/octocat/Hello-World/hooks')
.reply(200, [mockWebhookResponse]);
Get Operation
nock('https://api.github.com')
.get('/repos/octocat/Hello-World/hooks/12345678')
.reply(200, mockWebhookResponse);
Error Case
nock('https://api.github.com')
.get('/repos/octocat/Hello-World/hooks/999')
.reply(404, { message: 'Not Found' });
Mock Cleanup
✅ afterEach(() => nock.cleanAll())
Mock Verification
✅ expect(nock.isDone()).toBe(true)
Validation
✅ Only nock used ✅ HTTP level mocking ✅ Realistic responses ✅ Proper cleanup ✅ Mock verification
## Success Criteria
HTTP mocking implementation MUST meet all criteria:
- ✅ Only nock library used (no jest.mock, sinon, fetch-mock)
- ✅ All HTTP requests mocked at network level
- ✅ Realistic mock responses (match API format)
- ✅ Proper cleanup after tests (`nock.cleanAll()`)
- ✅ Mock verification in tests (`nock.isDone()`)
- ✅ Reusable mock patterns/fixtures where appropriate
- ✅ Request matching includes method, path, headers (when relevant)