Waiting for Emails
This guide covers efficient patterns for waiting for emails in your tests, including filtering, timeouts, and handling multiple emails.
Basic Waiting
Section titled “Basic Waiting”Wait for Any Email
Section titled “Wait for Any Email”Email email = inbox.waitForEmail();Uses the default timeout configured on the client (30 seconds by default).
Wait with Custom Timeout
Section titled “Wait with Custom Timeout”Email email = inbox.waitForEmail( EmailFilter.any(), Duration.ofSeconds(60));Wait with Filter
Section titled “Wait with Filter”Email email = inbox.waitForEmail( EmailFilter.subjectContains("Welcome"));Await vs Wait
Section titled “Await vs Wait”The library provides two patterns for handling timeouts:
waitForEmail - Throws on Timeout
Section titled “waitForEmail - Throws on Timeout”try { Email email = inbox.waitForEmail(Duration.ofSeconds(10)); // Process email} catch (TimeoutException e) { fail("Email not received within timeout");}awaitEmail - Returns Null on Timeout
Section titled “awaitEmail - Returns Null on Timeout”Email email = inbox.awaitEmail( EmailFilter.any(), Duration.ofSeconds(30));
if (email == null) { // Handle timeout gracefully System.out.println("No email received"); return;}
// Process emailFiltering While Waiting
Section titled “Filtering While Waiting”By Subject
Section titled “By Subject”// Contains substringEmail email = inbox.waitForEmail( EmailFilter.subjectContains("Password Reset"));
// Regex matchingEmail email = inbox.waitForEmail( EmailFilter.subjectMatches(Pattern.compile("Order #\\d+")));By Sender
Section titled “By Sender”// Contains substringEmail email = inbox.waitForEmail( EmailFilter.from("noreply@example.com"));
// Regex matchingEmail email = inbox.waitForEmail( EmailFilter.fromMatches(Pattern.compile(".*@example\\.com")));Custom Predicate
Section titled “Custom Predicate”// Wait for email with attachmentsEmail email = inbox.waitForEmail( EmailFilter.matching(e -> !e.getAttachments().isEmpty()));
// Wait for email with specific linkEmail email = inbox.waitForEmail( EmailFilter.matching(e -> e.getLinks().stream() .anyMatch(link -> link.contains("/verify"))));
// Wait for unread emailEmail email = inbox.waitForEmail( EmailFilter.matching(e -> !e.isRead()));Combined Filters
Section titled “Combined Filters”EmailFilter filter = EmailFilter.subjectContains("Invoice") .and(EmailFilter.from("billing@")) .and(EmailFilter.matching(e -> !e.getAttachments().isEmpty()));
Email email = inbox.waitForEmail(filter, Duration.ofSeconds(30));Using the Builder Pattern
Section titled “Using the Builder Pattern”// Build filter with builderEmailFilter filter = EmailFilter.builder() .subject("Welcome") .from("noreply@") .where(e -> e.getLinks().size() > 0) .build();
Email email = inbox.waitForEmail(filter);Waiting for Multiple Emails
Section titled “Waiting for Multiple Emails”Wait for Count
Section titled “Wait for Count”List<Email> emails = inbox.waitForEmailCount(3);Wait for Count with Timeout
Section titled “Wait for Count with Timeout”List<Email> emails = inbox.waitForEmailCount( 3, Duration.ofSeconds(60));Process Multiple Sequentially
Section titled “Process Multiple Sequentially”// Wait for confirmation emailEmail confirmation = inbox.waitForEmail( EmailFilter.subjectContains("Order Confirmation"), Duration.ofSeconds(30));
// Wait for shipping email (different email)Email shipping = inbox.waitForEmail( EmailFilter.subjectContains("Shipped"), Duration.ofSeconds(60));Wait Options Configuration
Section titled “Wait Options Configuration”For advanced control, use WaitOptions:
WaitOptions options = WaitOptions.builder() .filter(EmailFilter.subjectContains("Reset")) .timeout(Duration.ofSeconds(30)) .pollInterval(Duration.ofMillis(500)) // Custom poll interval .build();
Email email = inbox.waitForEmail(options);Configuration Options
Section titled “Configuration Options”| Option | Description |
|---|---|
filter | Email filter criteria |
timeout | Maximum wait time |
pollInterval | Poll interval for polling strategy |
Timeout Handling
Section titled “Timeout Handling”With Exception (Default)
Section titled “With Exception (Default)”@Testvoid shouldReceiveEmail() { try { Email email = inbox.waitForEmail(Duration.ofSeconds(10)); assertThat(email).isNotNull(); } catch (TimeoutException e) { fail("Email not received within timeout"); }}With Fallback
Section titled “With Fallback”Email email = inbox.awaitEmail( EmailFilter.subjectContains("Optional"), Duration.ofSeconds(5));
if (email != null) { processOptionalEmail(email);} else { useDefaultBehavior();}With Retry
Section titled “With Retry”Email email = null;int maxAttempts = 3;
for (int attempt = 1; attempt <= maxAttempts && email == null; attempt++) { System.out.println("Attempt " + attempt + " of " + maxAttempts); email = inbox.awaitEmail( EmailFilter.subjectContains("Welcome"), Duration.ofSeconds(10) );}
if (email == null) { fail("Email not received after " + maxAttempts + " attempts");}Real-World Examples
Section titled “Real-World Examples”Password Reset Flow
Section titled “Password Reset Flow”@Testvoid shouldReceivePasswordResetEmail() { // Trigger password reset userService.requestPasswordReset(inbox.getEmailAddress());
// Wait for email Email email = inbox.waitForEmail( EmailFilter.subjectContains("Password Reset"), Duration.ofSeconds(30) );
// Extract reset link String resetLink = email.getLinks().stream() .filter(link -> link.contains("/reset")) .findFirst() .orElseThrow(() -> new AssertionError("Reset link not found"));
assertThat(resetLink).startsWith("https://");
// Extract token from link URI uri = URI.create(resetLink); String query = uri.getQuery(); String token = Arrays.stream(query.split("&")) .filter(p -> p.startsWith("token=")) .map(p -> p.substring(6)) .findFirst() .orElseThrow();
assertThat(token).isNotEmpty();}Welcome Email Verification
Section titled “Welcome Email Verification”@Testvoid shouldReceiveWelcomeEmail() { // Register user userService.register(inbox.getEmailAddress(), "password123");
// Wait for welcome email Email email = inbox.waitForEmail( EmailFilter.subjectContains("Welcome"), Duration.ofSeconds(30) );
// Verify sender assertThat(email.getFrom()).isEqualTo("noreply@example.com");
// Verify content assertThat(email.getText()).contains("Thank you for signing up");
// Verify verification link exists assertThat(email.getLinks()) .anyMatch(link -> link.contains("/verify"));}Multi-Step Email Flow
Section titled “Multi-Step Email Flow”@Testvoid shouldReceiveOrderEmails() { // Place order String orderId = orderService.placeOrder(inbox.getEmailAddress(), cart);
// Wait for confirmation Email confirmation = inbox.waitForEmail( EmailFilter.subjectContains("Order Confirmation"), Duration.ofSeconds(30) ); assertThat(confirmation.getText()).contains(orderId);
// Ship the order orderService.shipOrder(orderId);
// Wait for shipping notification Email shipping = inbox.waitForEmail( EmailFilter.subjectContains("Shipped"), Duration.ofSeconds(30) ); assertThat(shipping.getText()).contains("tracking");
// Deliver the order orderService.deliverOrder(orderId);
// Wait for delivery confirmation Email delivered = inbox.waitForEmail( EmailFilter.subjectContains("Delivered"), Duration.ofSeconds(30) ); assertThat(delivered).isNotNull();}Email with Attachments
Section titled “Email with Attachments”@Testvoid shouldReceiveInvoice() { billingService.sendInvoice(inbox.getEmailAddress());
Email email = inbox.waitForEmail( EmailFilter.matching(e -> !e.getAttachments().isEmpty()), Duration.ofSeconds(30) );
Attachment pdf = email.getAttachments().stream() .filter(a -> a.getFilename().endsWith(".pdf")) .findFirst() .orElseThrow(() -> new AssertionError("PDF attachment not found"));
assertThat(pdf.getContentType()).isEqualTo("application/pdf"); assertThat(pdf.getSize()).isGreaterThan(0);}Two-Factor Authentication
Section titled “Two-Factor Authentication”@Testvoid shouldReceive2FACode() { // Trigger 2FA authService.requestTwoFactorCode(inbox.getEmailAddress());
// Wait for code email Email email = inbox.waitForEmail( EmailFilter.subjectContains("Verification Code"), Duration.ofSeconds(30) );
// Extract 6-digit code Pattern codePattern = Pattern.compile("\\b(\\d{6})\\b"); Matcher matcher = codePattern.matcher(email.getText());
assertThat(matcher.find()).isTrue(); String code = matcher.group(1);
// Verify the code boolean verified = authService.verifyCode(code); assertThat(verified).isTrue();}Advanced Patterns
Section titled “Advanced Patterns”Conditional Waiting
Section titled “Conditional Waiting”Only wait if email hasn’t already arrived:
// Check for existing emails firstOptional<Email> existing = inbox.listEmails().stream() .filter(e -> e.getSubject().contains("Target")) .findFirst();
Email target = existing.orElseGet(() -> inbox.waitForEmail(EmailFilter.subjectContains("Target")));Parallel Email Sending
Section titled “Parallel Email Sending”@Testvoid shouldReceiveAllParallelEmails() throws Exception { // Send multiple emails in parallel CompletableFuture.allOf( CompletableFuture.runAsync(() -> emailService.sendWelcome(inbox.getEmailAddress())), CompletableFuture.runAsync(() -> emailService.sendVerification(inbox.getEmailAddress())), CompletableFuture.runAsync(() -> emailService.sendNewsletter(inbox.getEmailAddress())) ).join();
// Wait for all three List<Email> emails = inbox.waitForEmailCount(3, Duration.ofSeconds(30)); assertThat(emails).hasSize(3);}Filtered Collection
Section titled “Filtered Collection”// Wait for emails and filter resultsList<Email> invoices = inbox.waitForEmailCount(5, Duration.ofSeconds(60)) .stream() .filter(e -> e.getSubject().contains("Invoice")) .collect(Collectors.toList());Time-Based Assertions
Section titled “Time-Based Assertions”@Testvoid emailShouldArriveQuickly() { Instant start = Instant.now();
// Trigger email emailService.sendImmediate(inbox.getEmailAddress());
// Wait for email Email email = inbox.waitForEmail(Duration.ofSeconds(10));
// Verify fast delivery Duration deliveryTime = Duration.between(start, email.getReceivedAt()); assertThat(deliveryTime.toSeconds()).isLessThan(5);}Testing Best Practices
Section titled “Testing Best Practices”Flake-Free Tests
Section titled “Flake-Free Tests”@Testvoid flakeFreeTest() { // 1. Use generous timeouts for CI environments Email email = inbox.waitForEmail(Duration.ofSeconds(60));
// 2. Use specific filters to avoid matching wrong emails Email specific = inbox.waitForEmail( EmailFilter.subjectContains("Exact Subject") .and(EmailFilter.from("specific@sender.com")), Duration.ofSeconds(30) );
// 3. Clear inbox between tests if sharing inbox.listEmails().forEach(Email::delete);}Fast Tests for Development
Section titled “Fast Tests for Development”@Testvoid fastDevelopmentTest() { // Shorter timeout for quick feedback Email email = inbox.waitForEmail(Duration.ofSeconds(5));}Descriptive Failures
Section titled “Descriptive Failures”@Testvoid descriptiveFailureTest() { try { Email email = inbox.waitForEmail( EmailFilter.subjectContains("Expected Subject"), Duration.ofSeconds(10) ); } catch (TimeoutException e) { // List what we did receive List<Email> received = inbox.listEmails(); String subjects = received.stream() .map(Email::getSubject) .collect(Collectors.joining(", "));
fail("Expected email with subject 'Expected Subject' but received: [" + subjects + "]"); }}Troubleshooting
Section titled “Troubleshooting”Timeout Errors
Section titled “Timeout Errors”- Increase timeout - Network delays can vary
- Check email sending - Verify the trigger actually sends
- Use SSE strategy - Faster than polling
- Check filters - Filter may be too restrictive
// Debug: List all emails to see what arrivedList<Email> all = inbox.listEmails();System.out.println("Received " + all.size() + " emails:");for (Email e : all) { System.out.println(" - " + e.getSubject() + " from " + e.getFrom());}Wrong Email Matched
Section titled “Wrong Email Matched”Use more specific filters:
// Too genericEmail email = inbox.waitForEmail(EmailFilter.from("@example.com"));
// More specificEmail email = inbox.waitForEmail( EmailFilter.from("noreply@example.com") .and(EmailFilter.subjectContains("Password Reset")));Flaky Tests
Section titled “Flaky Tests”- Use generous timeouts - At least 30 seconds for CI
- Use specific filters - Avoid matching wrong emails
- Clear inbox between tests - Prevent cross-test pollution
- Fresh inbox per test - Maximum isolation
@BeforeEachvoid setUp() { // Fresh inbox for each test inbox = client.createInbox();}
@AfterEachvoid tearDown() { inbox.delete();}Performance
Section titled “Performance”For fastest email detection:
// Use SSE strategyClientConfig config = ClientConfig.builder() .apiKey(apiKey) .baseUrl(baseUrl) .strategy(StrategyType.SSE) .build();
VaultSandboxClient client = VaultSandboxClient.create(config);Next Steps
Section titled “Next Steps”- Real-Time Monitoring - Subscription-based patterns
- Managing Inboxes - Inbox lifecycle
- Delivery Strategies - SSE vs Polling