Most sign-up flows look simple from a user's perspective.
Fill a form. Submit. Open your inbox. Click an activation link. Done.
From a testing perspective, things get more interesting. The registration form itself is easy to automate. The challenge starts when the application sends an email and expects the user to continue the journey outside the browser.
A lot of teams stop testing at the "Email sent successfully" message. Technically, that's enough to verify that the backend accepted the registration request.
But is it enough?
The gap between "email sent" and "user activated"
Imagine a bug slips into production:
- the activation email is never delivered
- the activation link is malformed
- the token is missing
- the email template accidentally points to the wrong environment
- an engineer changes a route and forgets to update the template
The registration page still works.
The API still returns 200 OK.
Your automated tests stay green.
Meanwhile, every new user gets stuck.
This is why some teams choose to test the entire activation journey instead of stopping at the registration form.
Should you test email content?
Usually, no.
Testing every sentence, heading, button label, or piece of marketing copy inside an email is often brittle and expensive to maintain.
Email content changes frequently. Product teams tweak wording. Marketing updates branding. Designers adjust layouts.
End-to-end tests should focus on behavior.
The important questions are:
- Was an email generated?
- Does it contain a valid activation link?
- Can the user complete activation using that link?
If those checks pass, you've validated the business flow.
There are exceptions.
For highly regulated industries, financial applications, healthcare systems, or products where email content contains critical legal information, additional verification may be justified.
For most SaaS applications, validating the activation link is enough.
Capturing activation emails during tests
One approach is to use a disposable mailbox service such as Restmail.
The application sends a real email. The test polls the mailbox API, extracts the activation URL, and continues the flow just like a real user would.
To make the example more relatable, let's imagine we're testing a modern open-source application such as the demo deployment of Appsmith or any self-hosted SaaS product that requires email verification after registration.
Playwright already provides everything needed for this workflow.
There's no need to install Axios, Fetch wrappers, or any additional HTTP client.
The built-in request fixture works perfectly for polling a mailbox API.
import { test, expect, APIRequestContext } from "@playwright/test";
async function pollForActivationLink(
request: APIRequestContext,
user: string,
timeout = 60000,
interval = 1500
): Promise<string> {
const url = `http://restmail.net/mail/${user}`;
const startTime = Date.now();
while (Date.now() - startTime < timeout) {
const response = await request.get(url);
if (response.ok()) {
const messages = await response.json();
if (messages?.length) {
const html: string = messages[0].html;
const match = html.match(
/(https?:\/\/[^"]+activate[^"]+)/
);
if (match) {
return match[1].replace("=\r\n", "");
}
}
}
await new Promise((r) => setTimeout(r, interval));
}
throw new Error(
`Timed out waiting for activation email for ${user}`
);
}
The helper continuously checks the mailbox until an activation email appears or the timeout is reached.
Once the link is extracted, the test can continue exactly as a user would.
const USER = "playwright-user";
test("registers and activates account", async ({
page,
request,
}) => {
await page.goto("https://demo-app.example.com/signup");
await page.fill("[name=email]", `${USER}@restmail.net`);
await page.fill("[name=username]", USER);
await page.fill("[name=password]", "Password1234");
await page.click("button[type=submit]");
const activationLink =
await pollForActivationLink(request, USER);
await request.delete(
`http://restmail.net/mail/${USER}`
);
await page.goto(activationLink);
await expect(page).toHaveURL(/activated|verified|welcome/);
});
What I like about this approach is that it verifies multiple systems at once.
The registration form works.
The backend generates a token.
The email service sends a message.
The template contains the correct URL.
The activation endpoint accepts the token.
The account becomes active.
That's a lot of coverage from a relatively small amount of code.
The trade-off
There is a downside.
Tests that depend on email delivery are slower and less deterministic than tests that only interact with the UI or backend APIs.
If your suite contains hundreds of tests, you probably don't want every registration scenario waiting for mailbox polling.
A common compromise is to keep one or two "happy path" activation tests and cover the rest of the registration logic at lower levels:
- unit tests for token generation
- integration tests for email templates
- end-to-end tests for the complete user journey
That gives confidence without turning the test suite into a waiting game.
Final thoughts
I wouldn't test every detail of an activation email.
I would test that a real user can receive it and successfully activate an account.
Those are very different goals.
The first validates content.
The second validates the business process.
When users complain that they cannot sign up, it's usually the business process that matters.
Top comments (0)