Selenium email testing with MailSlurp
Documentation navigation
Set up Selenium email and SMS tests with Java, explicit browser waits, message conditions, OTP and link extraction, AI matching, and reliable CI cleanup.
Use Selenium to request a signup, password reset, or phone verification, then use MailSlurp to receive the message and complete the journey. Your test controls the browser and calls the email or SMS API directly, so it can assert both delivery and the application's response to the delivered code or link.
Selenium email testing quick start
- Install Selenium and the MailSlurp SDK for your test language.
- Create a fresh inbox, or reserve a provisioned phone number for the scenario.
- Enter its address or number in your application and record the time before submitting.
- Wait for the expected message using the resource ID, timestamp, and relevant filters.
- Extract the code or link, continue in the same browser session, and assert the final application state.
- Close the browser and clean up test-owned resources even when an assertion fails.
Keep the complete journey in one test. Splitting signup, receive, and confirmation into ordered tests makes retries and parallel execution harder to isolate. The integration testing guide explains the inbox, email, phone, and SMS objects used below.
Setup
Java
The main example uses Java, Maven, JUnit 4, and Chrome. Add these dependencies to a Maven test project. These versions match the linked example project's dependency baseline; use compatible versions pinned by your own suite when integrating it.
<dependencies>
<dependency>
<groupId>com.mailslurp</groupId>
<artifactId>mailslurp-client-java</artifactId>
<version>17.0.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>4.25.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
</dependencies>
Use a JDK supported by your Selenium version, such as JDK 17 for this example, and have Chrome available on the runner. Selenium Manager can resolve the driver for new ChromeDriver(...); restricted CI runners may need the matching browser and driver installed or cached ahead of time. A Selenium Grid suite can keep its existing RemoteWebDriver setup.
Set MAILSLURP_API_KEY in your environment or CI secret settings using a key from the MailSlurp dashboard. The key stays in the test process. Configure your application's test backend to send real email to the generated address, and allow the runner to reach the MailSlurp API.
C#, Node.js, and Python
Use the corresponding SDK with your existing Selenium runner:
# C# / NUnit, MSTest, or SpecFlow
dotnet add package mailslurp
# Node.js
npm install --save-dev mailslurp-client
# Python
pip install mailslurp-client
The same resource and wait patterns apply in each language. See the C#, JavaScript, and Python guides for client initialization, and the SDK list for other languages.
Usage
Complete email verification test
Save the following as src/test/java/ReliableEmailTest.java in your Maven project. It creates an expiring inbox, signs up on the MailSlurp playground, waits for the confirmation email, extracts its code, confirms signup, and signs in.
import com.mailslurp.apis.InboxControllerApi;
import com.mailslurp.apis.WaitForControllerApi;
import com.mailslurp.clients.ApiClient;
import com.mailslurp.models.*;
import org.junit.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import java.time.Duration;
import java.time.OffsetDateTime;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static org.junit.Assert.*;
public class ReliableEmailTest {
@Test
public void verifySignupAndSignIn() throws Exception {
String apiKey = System.getenv("MAILSLURP_API_KEY");
assertNotNull("Set MAILSLURP_API_KEY", apiKey);
ApiClient client = new ApiClient();
client.setApiKey(apiKey);
client.setConnectTimeout(30_000);
client.setReadTimeout(70_000); // Longer than the 60-second message wait.
InboxControllerApi inboxes = new InboxControllerApi(client);
WaitForControllerApi messages = new WaitForControllerApi(client);
InboxDto inbox = inboxes.createInboxWithOptions(
new CreateInboxDto().expiresIn(600_000L)).execute();
WebDriver driver = null;
try {
driver = new ChromeDriver(new ChromeOptions().addArguments("--headless=new"));
WebDriverWait ui = new WebDriverWait(driver, Duration.ofSeconds(15));
String password = "Test-password-42!";
driver.get("https://playground.mailslurp.com");
ui.until(ExpectedConditions.elementToBeClickable(By.cssSelector(
"[data-test=sign-in-create-account-link]"))).click();
ui.until(ExpectedConditions.visibilityOfElementLocated(By.name("email")))
.sendKeys(inbox.getEmailAddress());
driver.findElement(By.name("password")).sendKeys(password);
OffsetDateTime since = OffsetDateTime.now();
driver.findElement(By.cssSelector("[data-test=sign-up-create-account-button]")).click();
Email email = messages.waitForMatchingFirstEmail(inbox.getId(),
new MatchOptions().addMatchesItem(new MatchOption()
.field(MatchOption.FieldEnum.SUBJECT)
.should(MatchOption.ShouldEnum.CONTAIN)
.value("Please confirm your email address")))
.since(since).timeout(60_000L).unreadOnly(true).execute();
assertTrue(email.getTo().contains(inbox.getEmailAddress()));
Matcher codeMatch = Pattern.compile("verification code is\\s+(\\d{6})\\b",
Pattern.CASE_INSENSITIVE).matcher(email.getBody() == null ? "" : email.getBody());
assertTrue("No verification code in email " + email.getId(), codeMatch.find());
String code = codeMatch.group(1);
ui.until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector(
"[data-test=confirm-sign-up-confirmation-code-input]"))).sendKeys(code);
ui.until(ExpectedConditions.elementToBeClickable(By.cssSelector(
"[data-test=confirm-sign-up-confirm-button]"))).click();
ui.until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector(
"[data-test=username-input]"))).sendKeys(inbox.getEmailAddress());
driver.findElement(By.cssSelector("[data-test=sign-in-password-input]")).sendKeys(password);
driver.findElement(By.cssSelector("[data-test=sign-in-sign-in-button]")).click();
assertTrue(ui.until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector(
"[data-test=greetings-nav]"))).isDisplayed());
} finally {
try {
if (driver != null) driver.quit();
} finally {
try {
inboxes.deleteInbox(inbox.getId()).execute();
} catch (Exception cleanupFailure) {
// Preserve the test failure; expiry remains a cleanup fallback.
System.err.println("Inbox cleanup failed: " + inbox.getId());
}
}
}
}
}
Run it with:
mvn -Dtest=ReliableEmailTest test
The example repository keeps this class in the com.mailslurp.examples package. When adapting the test, change the URL, selectors, subject, code format, and final authenticated-state assertion to match your application. Deleting a MailSlurp inbox does not delete the user created in your application's database; clean up that account separately when required.
Browser waits and message waits solve different problems
WebDriverWait with ExpectedConditions waits for DOM state, such as a visible input or clickable confirmation button. A MailSlurp wait holds an API request open until an email or SMS meets your conditions. A page load or successful click does not prove that the backend has sent the message.
Use explicit browser waits around transitions and avoid Thread.sleep as a delivery check. Keep Selenium's implicit wait at its default zero when using this pattern; Selenium's wait guidance cautions against mixing implicit and explicit waits.
Give each layer its own budget. The example allows 15 seconds for a UI condition, 60 seconds for a message, and 70 seconds for the API client's read timeout. A connection timeout only covers establishing the connection. Any test-runner or CI job timeout must cover the whole journey, including browser startup and cleanup.
Creating inboxes
Prefer a new inbox per test attempt. expiresIn provides a fallback if a runner crashes before teardown. Add names, descriptions, and tags when you need to find the test's resources in diagnostics:
// use names, description, and tags to identify an inbox
String randomString = String.valueOf(new Random().nextLong());
String customName = "Test inbox " + randomString;
String customDescription = "My custom description " + randomString;
String customTag = "test-inbox-" + randomString;
// create inbox with options so we can find it later
CreateInboxDto options = new CreateInboxDto()
.name(customName)
.description(customDescription)
.tags(Collections.singletonList(customTag));
InboxDto inbox = inboxControllerApi.createInboxWithOptions(options).execute();
The snippet assumes an initialized InboxControllerApi named inboxControllerApi and imports java.util.Random and java.util.Collections. Names and tags help identify ownership; they do not reserve an inbox against another worker. Use the returned inbox ID in API calls and its email address in the browser form.
Finding inboxes
For a workflow that requires a stable address, load the inbox by ID or look it up by address/name. Reserve it for that scenario and distinguish messages by time and a unique reference.
Get inbox by address or name
Using the inbox and inboxControllerApi above, look up the resource by ID or address. The last check demonstrates the exists flag when a name is absent:
// get inbox by id
InboxDto inboxById = inboxControllerApi.getInbox(inbox.getId()).execute();
// lookup inbox by address
InboxByEmailAddressResult inboxByAddress =
inboxControllerApi.getInboxByEmailAddress(inbox.getEmailAddress()).execute();
assertEquals(inboxByAddress.getInboxId(), inbox.getId());
// lookup inbox by name
InboxByNameResult inboxByName =
inboxControllerApi.getInboxByName("Non-existing inbox").execute();
assertFalse(inboxByName.getExists());
Search for an inbox
This example uses customTag assigned when creating an inbox and the same inboxControllerApi:
PageInboxProjection inboxSearchResult = inboxControllerApi.searchInboxes(
new SearchInboxesOptions()
.search(customTag)
).execute();
assertEquals(inboxSearchResult.getNumberOfElements(), Integer.valueOf(1));
assertEquals(inboxSearchResult.getContent().get(0).getId(), inbox.getId());
Assert that lookup returns the intended resource rather than choosing an arbitrary first result. Fresh inboxes are usually simpler for signup tests; persistent inboxes are useful when the application requires a preconfigured account or allowlisted address.
Waiting for messages
| Scenario | Java wait method | Returned value |
|---|---|---|
| One message in an isolated inbox | waitForLatestEmail |
Full Email |
| A particular subject, sender, or recipient | waitForMatchingFirstEmail |
Full Email |
| Several emails matching a reference | waitForMatchingEmails |
List<EmailPreview> |
| Combined count, time, and field conditions | waitFor |
List<EmailPreview> |
| One SMS on a reserved phone | waitForLatestSms |
Full SmsDto |
| SMS filtered by body or sender | waitForSms |
List<SmsPreview> |
Capture OffsetDateTime.now() before triggering the application's send and pass it as since. unreadOnly(true) excludes already-read messages, but an unread message from a previous run is still unread. Use both a time boundary and test-owned resources. Reading a full email can change its read state; retain that object for later assertions instead of waiting for the same unread message again.
Wait for multiple messages and conditions
For an order that sends two notifications, include its unique orderReference in the expected subject filter. This snippet assumes client, inboxId, since, and orderReference are in scope and uses com.mailslurp.models.*, com.mailslurp.apis.*, java.util.List, and JUnit assertions:
WaitForControllerApi messages = new WaitForControllerApi(client);
List<EmailPreview> previews = messages.waitFor(new WaitForConditions()
.inboxId(inboxId).since(since).timeout(60_000L).unreadOnly(true)
.count(2).countType(WaitForConditions.CountTypeEnum.ATLEAST)
.addMatchesItem(new MatchOption()
.field(MatchOption.FieldEnum.SUBJECT)
.should(MatchOption.ShouldEnum.CONTAIN).value(orderReference)))
.execute();
assertTrue(previews.size() >= 2);
EmailControllerApi emails = new EmailControllerApi(client);
for (EmailPreview preview : previews) {
Email fullEmail = emails.getEmail(preview.getId()).execute();
assertNotNull(fullEmail.getBody());
}
The wait returns previews; fetch each full email before checking its body or attachments. Add assertions that distinguish the expected receipt and dispatch notification and compare their order details to the test's input. A count of two by itself could be two copies of the same message.
ATLEAST is suitable for waiting until a minimum count arrives. It does not prove that no duplicates will arrive later. For a duplicate-send test, define an observation period, then inspect all matching messages within that period and assert the final count. See all wait conditions for sorting, nth-email waits, and time windows.
Searching for messages
Search methods inspect messages that already exist; they do not wait for a future send. Use them to investigate a failed wait or inspect an already completed workflow:
Here mailslurpClient is the configured ApiClient called client in the complete test:
EmailControllerApi emailController = new EmailControllerApi(mailslurpClient);
PageEmailProjection emailSearch = emailController.searchEmails(
new SearchEmailsOptions()
.searchFilter("Test subject")
).execute();
This example's search is account-wide. Scope diagnostic queries to the test inbox or unique reference before interpreting a result, and do not assume the first result belongs to the current test.
Getting content
The complete test already receives a full Email. When another operation gives you a preview or ID, load the full email with new EmailControllerApi(client).getEmail(emailId).execute(). Check the recipient and business content before using a code or URL.
Extract codes
A regex is useful for a stable template you control. Anchor it to surrounding text so an order number, date, or support reference is not mistaken for an OTP. Keep the result as a string to preserve leading zeros.
The following API operation applies the regex on the server. This helper body assumes client and emailId are available:
EmailControllerApi emails = new EmailControllerApi(client);
EmailContentMatchResult result = emails.getEmailContentMatch(emailId,
new ContentMatchOptions().pattern("verification code is\\s+(\\d{6})\\b"))
.execute();
// Index 0 contains the full match; index 1 contains the code capture group.
assertNotNull(result.getMatches());
assertTrue("No verification code in email " + emailId, result.getMatches().size() > 1);
String code = result.getMatches().get(1);
assertTrue(code.matches("\\d{6}"));
return code;
The full regex match is at index 0 and the code capture group is at index 1. Java string patterns require doubled backslashes. Update the phrase, case handling, and length for the actual email. If markup interrupts the text or several plausible codes appear, query a specific HTML element or use AI extraction with evidence.
Get links
For password resets and magic links, extract the delivered URLs and choose the expected application destination. This snippet assumes client, emailId, and driver are in scope. It additionally uses java.net.URI, java.util.List, and java.util.stream.Collectors:
List<String> links = new EmailControllerApi(client).getEmailLinks(emailId).execute().getLinks();
List<String> verificationLinks = links.stream().filter(link -> {
URI url = URI.create(link);
return "https".equals(url.getScheme()) && "app.example.com".equals(url.getHost())
&& (url.getPort() == -1 || url.getPort() == 443)
&& "/auth/verify".equals(url.getPath());
}).collect(Collectors.toList());
assertEquals(1, verificationLinks.size());
driver.get(verificationLinks.get(0));
WebDriverWait ui = new WebDriverWait(driver, Duration.ofSeconds(15));
assertTrue(ui.until(ExpectedConditions.visibilityOfElementLocated(
By.cssSelector("[data-test=account-home]"))).isDisplayed());
Replace the example host, path, and final selector. Keep the requesting browser session when the flow binds a link to cookies or state. Avoid a preliminary HTTP request to a single-use authentication URL, as that can consume the token before Selenium visits it. Complete the password change or login and assert the resulting access.
Query HTML
For a known template, a CSS or XPath query can locate the CTA, receipt total, or verification element more precisely than a pattern over raw HTML. The content extraction recipes show HTML queries. Use attachment operations for attached files and check their filenames, content types, and downloaded content separately from the email body.
A browser view of email HTML checks that HTML in the chosen browser. For real email-client rendering, use device previews for Gmail, Outlook, Apple Mail, and the targets relevant to your customers.
Receive SMS and verify a phone number
Provision a phone number in MailSlurp and set MAILSLURP_PHONE_NUMBER_ID to its UUID. Reserve the number for the scenario; do not let parallel workers choose the first phone from the same account list. The API needs the resource ID, while the application's form needs the dialable number and country code.
This helper body uses the same configured client and driver as the email test, plus PhoneControllerApi, java.util.UUID, and the Java imports shown above. Navigate to your application's phone form first and replace the selectors:
String phoneId = System.getenv("MAILSLURP_PHONE_NUMBER_ID");
assertNotNull("Set MAILSLURP_PHONE_NUMBER_ID", phoneId);
UUID phoneNumberId = UUID.fromString(phoneId);
PhoneNumberDto phone = new PhoneControllerApi(client).getPhoneNumber(phoneNumberId).execute();
WebDriverWait ui = new WebDriverWait(driver, Duration.ofSeconds(15));
// Replace these selectors with your application's phone verification form.
ui.until(ExpectedConditions.visibilityOfElementLocated(By.name("phone")))
.sendKeys(phone.getPhoneNumber());
OffsetDateTime since = OffsetDateTime.now();
driver.findElement(By.cssSelector("[data-test=send-code]")).click();
SmsDto sms = new WaitForControllerApi(client).waitForLatestSms(
new WaitForSingleSmsOptions().phoneNumberId(phoneNumberId).since(since)
.timeout(60_000L).unreadOnly(true)).execute();
Matcher match = Pattern.compile("\\b(\\d{6})\\b").matcher(sms.getBody() == null ? "" : sms.getBody());
assertTrue("No verification code in SMS " + sms.getId(), match.find());
ui.until(ExpectedConditions.visibilityOfElementLocated(By.name("code"))).sendKeys(match.group(1));
ui.until(ExpectedConditions.elementToBeClickable(By.cssSelector("[data-test=verify-code]"))).click();
assertTrue(ui.until(ExpectedConditions.visibilityOfElementLocated(
By.cssSelector("[data-test=phone-verified]"))).isDisplayed());
The extraction pattern assumes a single six-digit candidate. Use surrounding text or AI extraction if the message includes several numbers. Adapt phone formatting to the form's country-code controls rather than unconditionally stripping a prefix.
Wait for a specific SMS
For multiple message types on a phone, replace the latest-SMS wait with body matching. This alternative uses client, phoneNumberId, and the timestamp captured before the SMS action:
List<SmsPreview> previews = new WaitForControllerApi(client).waitForSms(
new WaitForSmsConditions().phoneNumberId(phoneNumberId).since(since)
.timeout(60_000L).unreadOnly(true).count(1L)
.addMatchesItem(new SmsMatchOption()
.field(SmsMatchOption.FieldEnum.BODY)
.should(SmsMatchOption.ShouldEnum.CONTAIN)
.value("Your verification code"))).execute();
assertFalse(previews.isEmpty());
SmsDto sms = new SmsControllerApi(client).getSmsMessage(previews.get(0).getId()).execute();
Add a FROM match for a known sender. waitForSms returns previews, so fetch the full SMS before extracting content. Do not run both unread-only waits sequentially against the same message. Serialize access to persistent numbers or allocate one per worker, and release your reservation after the test rather than deleting a number used by other suites.
See the Java SMS playground example for a provider-specific signup flow. Authenticator-app codes use a different mechanism: see virtual TOTP devices.
AI matching and extraction
Use an exact filter when you know the subject, sender, or reference. A semantic match is useful when localization or template changes alter wording while the message's purpose remains the same. Keep message selection separate from assertions about its correctness: select the receipt first, then assert its amount, so an incorrect receipt produces a visible failure.
Wait for a message and extract its code
The following Java helper body calls the HTTP API using OkHttp and Gson, which are included by the example's MailSlurp Java dependency. It assumes client, apiKey, inboxId, and since are in scope and imports okhttp3.*, com.google.gson.*, java.util.UUID, and java.util.concurrent.TimeUnit.
Use this as an alternative to the deterministic email wait and regex in the complete test. Its returned string is the code to enter in Selenium:
JsonObject scope = new JsonObject();
com.google.gson.JsonArray inboxIds = new com.google.gson.JsonArray();
inboxIds.add(inboxId.toString());
scope.add("inboxIds", inboxIds);
JsonObject match = new JsonObject();
match.addProperty("prompt", "The account signup email containing the current confirmation code");
JsonObject body = new JsonObject();
body.add("scope", scope);
body.addProperty("since", since.toString());
body.addProperty("timeout", 120_000);
body.add("match", match);
body.addProperty("extractionPreset", "OTP_CODE");
OkHttpClient http = client.getHttpClient().newBuilder()
.readTimeout(130, TimeUnit.SECONDS).callTimeout(140, TimeUnit.SECONDS).build();
Request request = new Request.Builder()
.url("https://api.mailslurp.com/ai/messages/wait")
.header("x-api-key", apiKey)
.header("Idempotency-Key", UUID.randomUUID().toString())
.post(RequestBody.create(MediaType.parse("application/json"), body.toString()))
.build();
try (Response response = http.newCall(request).execute()) {
assertTrue("MailSlurp HTTP " + response.code(), response.isSuccessful());
JsonObject result = JsonParser.parseString(response.body().string()).getAsJsonObject();
assertTrue(result.get("summary").getAsString() + " (" + result.get("evaluationId").getAsString() + ")",
result.get("successful").getAsBoolean());
String code = result.getAsJsonObject("data").get("code").getAsString();
assertTrue(code.matches("\\d{6}"));
assertTrue(result.getAsJsonObject("extractionEvidence").getAsJsonArray("/code").size() > 0);
return code;
}
For SMS, use scope.phoneNumberIds in place of scope.inboxIds. Check successful before using data; a successful HTTP response can still contain FAIL, INCONCLUSIVE, or a timeout outcome. Validate the application's code format and /code source evidence, then complete the browser flow. Do not repeat an ambiguous evaluation until it happens to pass.
The API wait allows 120 seconds, so its HTTP read and overall call timeouts are longer. Increase the enclosing test budget accordingly. Retain evaluationId, status, and usage diagnostics for failures. If retrying an interrupted HTTP request, reuse its idempotency key and original body; a new key requests another evaluation.
Extract structured values from a known message
If you already have an email ID, send an extraction request to POST /ai/messages/extract. This body requests an exact reset URL and expiry phrase:
{
"message": { "type": "EMAIL", "id": "YOUR_EMAIL_ID" },
"outputSchema": {
"type": "OBJECT",
"required": ["resetUrl", "expiryText"],
"properties": {
"resetUrl": {
"type": "STRING",
"description": "The exact password reset URL intended for the recipient"
},
"expiryText": {
"type": "STRING",
"description": "The exact phrase stating when that reset link expires"
}
}
},
"timeout": 60000
}
Use the same authenticated HTTP pattern as the wait, change the endpoint and body, and allow a transport budget greater than 60 seconds. Require successful, inspect evidence for /resetUrl and /expiryText, compare the expiry text to the intended policy, and validate the URL's destination before navigating. For SMS use type: "SMS" and the SMS ID.
Choose exactly one extraction selector: extractionPreset, outputSchema, or transformId. A receipt schema could extract an order ID, currency, total, and line items for comparison with the order fixture. See AI assertions and extraction for named checks, inconclusive outcomes, budgets, and source evidence.
Parallel tests, retries, and CI troubleshooting
The Page Object Model guide for E2E MFA testing includes a complete Selenium and TestNG project with page objects, message helpers, email and SMS scenarios, and per-test cleanup.
Give each test a separate WebDriver session and inbox. Avoid static mutable inbox, email, or code fields shared across tests. Retry the full journey with a new inbox; a mid-flow retry can otherwise submit a code for an account created by the previous attempt. Persistent phone scenarios also need application-state cleanup or a dedicated existing-user flow.
Capture a screenshot and relevant browser logs on failure, plus inbox/phone ID, message ID, send time, and expected filters. Keep API keys, full message bodies, OTPs, and authentication URLs out of broadly shared reports. Inbox expiry is a fallback; normal teardown should still call driver.quit() and delete test-owned inboxes.
| Failure | What to inspect |
|---|---|
| Element is missing after a click | Wait explicitly for the next visible or clickable element; page load is not application readiness. |
| MailSlurp wait ends early with a socket timeout | Set the API read timeout longer than the message wait, separately from connection timeout. |
| Email is present but the wait times out | Check inbox ID, timestamp, exact filter values, and whether another process marked it read. |
| Previous OTP is returned | Use a fresh inbox, a send-time boundary, and isolated phone reservations. |
| OTP is found but rejected | Check leading zeros, expiry, resend behavior, and the account/session that requested it. |
| Test passes alone but fails in parallel | Remove shared browser/account state and competing inbox or phone consumers. |
| AI returns no usable data | Assert successful and inspect the evaluation's status and reason; never submit null or guessed values. |
For resend testing, capture a new timestamp before the resend action and assert the application's documented rules for both the newest and earlier codes. For expired-link testing, control expiry in your test backend where possible and assert the rejection screen. Delivery alone is only the first half of either test.
Examples
Browse working projects for your language and runner:
csharp-dotnet-core2-selenium
Csharp Specflow Mstest Seleniumcsharp example repository: csharp-specflow-mstest-selenium
Java Maven Seleniumjava example repository: java-maven-selenium
Java Testng Seleniumjava example repository: java-testng-selenium
Ruby Capybara Cucumber Seleniumruby example repository: ruby-capybara-cucumber-selenium
Rust Selenium Email Testingrust example repository: rust-selenium-email-testing
Totp Mfa Auth0 Seleniumtotp example repository: totp-mfa-auth0-selenium
Resources
- Java Selenium email, SMS, and message recipes
- Java TestNG Selenium project
- C# Selenium and SpecFlow project
- Ruby Capybara and Cucumber project
- Wait-for methods and troubleshooting
- Transactional-flow testing techniques
Tutorial
Reserve numbers for parallel SMS tests
Use phone pools and leases when multiple workers need numbers. Acquire a lease in setup, pass the number into your page object, wait using its phone ID and a fresh since timestamp, and release the lease in teardown. See examples by task for related recipes.
Running this code needs an API key. Get one free