
Goodbye Flaky Email Tests: Automate Everything with Cypress & Playwright (and Keep Your Hair)
Testing a registration flow shouldn't require a doctorate in patience. Discover how to use the JunkMail API to make your E2E tests finally reliable.
You know that feeling of deep, profound loneliness?
It’s the one you get when your E2E test crashes for the 4th time in a row because your mock SMTP server decided to take a nap. Or worse, the moment you have to explain to your Product Owner that the production bug went unnoticed because "we simulate email sending in the test environment, we don't actually do it."
Let's be honest: testing emails in a user flow (registration, password reset, OTP) is often a developer's nightmare. It’s slow, it’s brittle ("flaky"), and frankly, it’s a pain.
But what if I told you there’s a world where your email tests are as fast and reliable as your unit tests? A world where you can summon a real address, receive a real email, and click a real link, all in a few milliseconds?
Welcome to the future. Welcome to JunkMail.
Why Your Current Tests Hate You
Generally, there are two schools of thought:
- The "Mock Everything" School: You intercept the API request and tell your frontend, "Don't worry, the email was sent." Except the day SendGrid changes its config or your HTML template breaks, you see absolutely nothing.
- The "Mailtrap / Mailhog" School: It’s better. But it’s often slow, limited in volume, and doesn't test real deliverability (SPF, DKIM, DMARC... those alphabet soup acronyms that determine if an email actually arrives).
We need a third way: a real, ephemeral infrastructure that is API-driven.
The Secret Weapon: JunkMail Business API
Imagine being able to invoke a freshly created mailbox with a snap of your fingers (or rather, a POST call), use it, and let it vanish into the digital void once your test is done. Clean. Efficient.
That’s exactly what we’re going to do.
Step 1: Summon the Spirits (Create an Address)
Before launching Cypress or Playwright, we need a target. Not test@test.com (poor guy already gets enough spam), but a unique address for this specific test run.
// Setup script (or in your beforeEach)
const response = await fetch('https://api.junkmail.site/v1/addresses', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_SHINY_BUSINESS_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({ alias: 'e2e-signup-test' })
});
const { data } = await response.json();
const myMagicAddress = data.address; // e.g., xkcd42@junkmail.siteBoom. You have an address. It’s alive. It’s breathing.
Cypress: The Art of Waiting Without Losing Your Mind
Cypress is great, but it doesn't like stepping out of the browser. To check our emails, we’ll need to use cy.request and a bit of recursive magic.
Let’s create a custom command, because we are civilized people:
// cypress/support/commands.js
Cypress.Commands.add('waitForMyPreciousEmail', (addressId) => {
const check = () => {
return cy.request({
method: 'GET',
url: `https://api.junkmail.site/v1/emails?address_id=${addressId}`,
headers: { 'Authorization': `Bearer ${Cypress.env('JUNKMAIL_KEY')}` }
}).then((res) => {
// If the array isn't empty, we win!
if (res.body.data.length > 0) return res.body.data[0];
// Otherwise, take a breath and try again
cy.wait(1000);
return check();
});
};
return check();
});And now, the test that actually brings joy:
it('should allow a full registration without a hitch', () => {
// 1. Fill out the form
cy.visit('/signup');
cy.get('input[name="email"]').type(testEmail);
cy.get('button[type="submit"]').click();
// 2. The moment of truth
cy.waitForMyPreciousEmail(testAddressId).then((email) => {
expect(email.subject).to.contain('Welcome aboard!');
// 3. Surgical extraction of the validation link
const validationLink = /href="([^"]+)"/.exec(email.bodyHtml)[1];
// 4. Closing the loop
cy.visit(validationLink);
cy.contains('Account confirmed').should('be.visible');
});
});It’s fluid. It’s robust. It’s beautiful.
Playwright: For Those Who Love await
With Playwright, it’s even simpler. No need for complex commands; modern JavaScript does the heavy lifting.
test('Password Reset: The Crash Test', async ({ page }) => {
// ... skip the form filling steps ...
await page.click('#reset-password-btn');
// The Polling loop
// Let's give it 10 tries of 2 seconds each. Let's be generous.
let foundEmail = null;
for (let i = 0; i < 10; i++) {
const res = await api.get(`/emails?address_id=${addressId}`); // Pseudo-code for clarity
if (res.data.length > 0) {
foundEmail = res.data[0];
break;
}
await page.waitForTimeout(2000);
}
expect(foundEmail).not.toBeNull();
expect(foundEmail.subject).toBe('Reset your password (fast!)');
});Conclusion: End the Suffering
Email testing shouldn't be the most painful part of your CI/CD pipeline. With JunkMail Business, you transform a random chore into an exact science.
Your tests turn green, your QA team smiles again, and you can finally go home on time.
Isn't life grand?
Ready to scale? Upgrade to JunkMail Business and get unlimited API access.