How do we write clean unit and integration tests for email dispatch logic without hitting live network endpoints or spending API quota in CI/CD pipelines?
Testing Email Integration Logic in CI/CD
Making live HTTP calls during automated unit test runs slows down test suite execution, exhausts rate limits, and causes transient network failures in CI/CD pipelines.
Method 1: Mocking Global Fetch in Vitest / Jest
import { vi, test, expect } from 'vitest';
import { sendWelcomeEmail } from './email';
test('sends welcome email with correct payload', async () => {
const fetchMock = vi.fn().mockResolvedValue({
ok: true,
json: async () => ({ id: 'msg_test_123' })
});
global.fetch = fetchMock;
await sendWelcomeEmail('test@example.com', 'Alex');
expect(fetchMock).toHaveBeenCalledWith(
'https://api.autosend.com/v1/mails/send',
expect.objectContaining({
method: 'POST',
body: expect.stringContaining('test@example.com')
})
);
});
Method 2: MSW (Mock Service Worker) for Integration Tests
Interfacing with MSW allows intercepting REST calls at the network boundary without modifying application code:
import { http, HttpResponse } from 'msw';
import { setupServer } from 'msw/node';
export const handlers = [
http.post('https://api.autosend.com/v1/mails/send', async ({ request }) => {
const body = await request.json();
return HttpResponse.json({
success: true,
messageId: 'msg_mock_456'
}, { status: 200 });
})
];
export const server = setupServer(...handlers);