DEV Community

Cover image for TestFly Part 10: Email and 2FA testing with Mailhog, Mailtrap and IMAP
Hakan GÜL
Hakan GÜL

Posted on Originally published at hakangul.lovable.app

TestFly Part 10: Email and 2FA testing with Mailhog, Mailtrap and IMAP

End-to-end tests for user registration, password resets and two-factor authentication (2FA) often fail because email delivery is awkward to test. TestFly ships mailbox clients for Mailhog, Mailtrap, Outlook Graph and generic IMAP. In one automated test you can fetch incoming email, pull an OTP code out with a regex and follow the verification link.

Why email verification gets in the way of E2E tests

Almost every SaaS platform asks for email validation at signup or before sensitive operations. Automation suites usually have trouble with it:

  • Public inboxes such as Gmail enforce rate limits, CAPTCHAs and bot detection.
  • Developers often turn email verification off in test environments, so regressions in that flow reach production unnoticed.
  • Third-party email libraries need MIME parsing, socket handling and hand-written wait loops.

TestFly puts an email() helper on BaseTest. It hides the protocol details and runs its asynchronous polling through the central WaitEngine.

Connecting to a mailbox

Local mail catchers and enterprise cloud providers are both configured in testfly.yml:

```yaml title="testfly.yml"
email:
provider: mailhog # options: mailhog, mailtrap, outlook, imap
host: localhost
port: 8025
pollIntervalSeconds: 2
timeoutSeconds: 30




## Testing signup with 2FA / OTP in Java

The example below signs up through the web UI, waits for the verification email, extracts the 6-digit OTP code and completes the login:



```java
package io.testfly.examples.email;

import io.testfly.email.EmailMessage;
import io.testfly.test.BaseTest;
import org.testng.annotations.Test;

public class EmailVerificationTest extends BaseTest {

    @Test
    public void testUserRegistrationWithEmailConfirmation() {
        String testEmail = "user_" + System.currentTimeMillis() + "@testfly.local";

        // 1. Fill registration form on WebUI
        open("/register");
        find("#email-input").type(testEmail);
        find("#submit-btn").click();

        // 2. Wait for confirmation email to arrive
        EmailMessage message = email().waitForMessage(query -> 
            query.to(testEmail).subjectContains("Welcome to TestFly")
        );

        // 3. Extract OTP code or activation link via Regex
        String otpCode = message.extractRegex("Your verification code is: (\\d{6})");

        // 4. Enter OTP in browser and confirm
        find("#otp-input").type(otpCode);
        find("#verify-btn").click();

        assertThat(find("#welcome-banner")).isVisible();
    }
}
Enter fullscreen mode Exit fullscreen mode

Supported mail providers

Provider Protocol Typical Use Case
Mailhog HTTP REST API Local Docker environments, CI/CD runners
Mailtrap REST API & SMTP Staging environments, shared team sandboxes
Outlook Graph Microsoft Graph API Enterprise Office 365 test mailboxes
Generic IMAP SSL/TLS IMAP Custom corporate email servers

FAQ

Does TestFly poll the mailbox asynchronously?

Yes. It checks the inbox at a configurable interval until a message matching your criteria arrives or the timeout runs out.

Can I extract HTML links and click them directly?

Yes. message.extractLink("Confirm Email") returns the verification URL, and you can go to it right away with open(url).

Does this work with temporary throwaway email addresses?

Yes. Generate dynamic inbox aliases (for example user+123@domain.com) and filter for them with query.to(...).


Originally published on hakangul.lovable.app.

Top comments (0)