Email testing with Jest
Open an inbox in beforeEach, delete it in afterEach, and give Jest a timeout long enough for real mail to arrive.
How it fits together
Nothing special is needed beyond one thing people forget: Jest's default five second timeout is shorter than email delivery. Raise it on the tests that wait for mail.
The test
The third argument to test is the per-test timeout, in milliseconds.
// signup.test.js
import { MailFlat } from "@mailflat/sdk";
const mf = new MailFlat({ apiKey: process.env.MAILFLAT_API_KEY });
let inbox;
beforeEach(async () => {
inbox = await mf.create({
prefix: `jest-${Date.now().toString(36)}`,
retentionHours: 2,
});
});
afterEach(async () => {
await inbox.delete();
});
test(
"signup sends a verification code",
async () => {
await api.post("/signup", { email: inbox.address });
const otp = await inbox.waitForOtp({ timeout: 60_000 });
expect(otp).toMatch(/^\d{6}$/);
},
70_000, // longer than the waitForOtp timeout
);
Or raise it for the whole file
jest.setTimeout(70_000);
Worth knowing
Always set the Jest timeout HIGHER than the waitForOtp timeout, or Jest kills the test first and you lose the useful error message.
Jest runs test files in parallel workers, so one inbox per test file is the minimum isolation.
See also
JavaScript
Zero dependencies, built on the runtime's own fetch. Drop it into Jest, Vitest, Playwright, Cypress or a Node script.
Waiting and timeoutsMail is asynchronous, so every email test waits for something. The difference between a solid suite and a flaky one is how that wait is written.
Testing & CIGive every CI run its own real inbox: the four API calls, parallel shards, teardown, and how to keep an end-to-end suite fast and honest.