End-to-end Testing
How-tos & Guides
12 min read

How to test SMS workflows in Selenium

Create automated tests for workflows that span both web and SMS, such as two-factor authentication scenarios, using Selenium WebDriver.

Antonello Zanini
Published August 17, 2022

Many web applications now rely on your phone number for authentication or to deliver critical information. Specifically, these applications tend to send SMS messages to your personal phone number. Considering how rarely a person changes their phone number in life and how much we check our phones, SMS messages are one of the best ways to authenticate a user, get in touch with them, or communicate something urgent.

One of the most common workflows where automatic SMS is used is when a user logs in to a service. If two-factor authentication is enabled, an automated SMS containing a secure code is sent to the phone number of the user. Similarly, automated SMS workflows are used when a new user registers with an online service. Registration forms frequently ask for a personal phone number. After signing up, you will be asked to confirm your phone number via an automated SMS that contains a short verification code (often called a one-time password or OTP).

In this article, you will learn how to create an automated test for such an SMS workflow using Selenium WebDriver. Specifically, you will see how to create a Selenium test in Java that fills out a registration form involving a phone number field, waits for an SMS to arrive at the given phone number, extracts the verification code from the SMS, and uses the extracted code to complete the registration process.

What are the common scenarios that include automated SMS messages?

There are several possible end-to-end scenarios where automated SMS messages are used. Here is a list of the most common scenarios that spans both web and SMS:

Why you should test your SMS workflows

There are several reasons why SMS workflows are critical for your website or web application, and you should test them accordingly. Let’s dig into the three most important ones.

How To Test an SMS Workflow with Selenium

Let’s now learn how to test a registration SMS workflow with Selenium. In this step-by-step tutorial, you will see how you can use Selenium to retrieve the verification code provided to the newly subscribed user via SMS and check if it corresponds to the expected one. This will require you an SMS API provider, such as Twilio. Integrating it into your Selenium test to achieve the desired goal only takes a few lines of code.

Prerequisites

Before getting started, you need:

Now, you need to add the Twilio Java SDK to your project’s dependencies.

If you are a Maven user, place the following lines in your pom.xml file:

<dependency>
  <groupId>com.twilio.sdk</groupId>
  <artifactId>twilio</artifactId>
  <version>8.9.0</version>
</dependency>

If you are a Gradle user, add the next line in the dependencies block of your build.gradle file:

implementation group: "com.twilio.sdk", name: "twilio", version: "8.9.0"

You can learn more about how to integrate the Twilio Java SDK in Maven and Gradle here.

Otherwise, you can manually download a pre-built .jar Twilio Java SDK file from here and load it as an external library in your project. Follow this page to learn how to add a .jar module dependency in IntelliJ IDEA.

You are now ready to start using Twilio in your Selenium WebDriver Java script.

1. Connecting to the Registration Page

First, use Selenium to connect to the registration page of the website or web app you want to test the SMS workflow. You can achieve this as follows:

import org.openqa.selenium.*;
import org.openqa.selenium.chrome.ChromeDriver;

public class Main {
    public static void main(String[] args) {
        // setting the system property for the Chrome Driver
        System.setProperty("webdriver.chrome.driver", "<the_path_to_your_chrome_driver>");

        // initializing the Selenium WebDriver ChromeDriver class
        WebDriver driver = new ChromeDriver();

        // connecting to the registration page
        driver.get("http://localhost/registration-page");
    }
}

This is what http://localhost/registration-page looks like:

As you can see, it is a simple registration form based on a free Bootstrap 5 template that requires the user to insert their phone number. When testing an existing website, replace http://localhost/registration-page with the actual registration page URL.

2. Filling out the Registration form

Now, fill out the registration form and submit it with Selenium to trigger the SMS workflow.

This is what the HTML of the sample registration form seen before looks like:

<form method="POST" class="register-form" id="register-form" action="confirmation-sms-sent.php">
  <div class="form-group">
    <label for="name"><i class="zmdi zmdi-account material-icons-name"></i></label>
    <input type="text" name="name" id="name" placeholder="Your Name" />
  </div>
  <div class="form-group">
    <label for="email"><i class="zmdi zmdi-email"></i></label>
    <input type="email" name="email" id="email" placeholder="Your Email" />
  </div>
  <div class="form-group">
    <label for="phone-number"><i class="zmdi zmdi-phone"></i></label>
    <input type="phone-number" name="phone-number" id="phone-number" placeholder="Your Phone Number" />
  </div>
  <div class="form-group">
    <label for="password"><i class="zmdi zmdi-lock"></i></label>
    <input type="password" name="password" id="password" placeholder="Password" />
  </div>
  <div class="form-group">
    <label for="re_password"><i class="zmdi zmdi-lock-outline"></i></label>
    <input type="password" name="re_password" id="re_password" placeholder="Repeat password" />
  </div>
  <div class="form-group">
    <input type="checkbox" name="agree-term" id="agree-term" class="agree-term" />
    <label for="agree-term" class="label-agree-term"
      ><span><span></span></span>I agree all statements in <a href="#" class="term-service">Terms of service</a></label
    >
  </div>
  <div class="form-group form-button">
    <input type="submit" name="signup" id="signup" class="form-submit" value="Register" />
  </div>
</form>

Therefore, the Selenium script to fill out the form and submit it should implement the following logic:

import org.openqa.selenium.*;
import org.openqa.selenium.chrome.ChromeDriver;

public class Main {
    public static void main(String[] args) {
        // setting the system property for the Chrome Driver
        System.setProperty("webdriver.chrome.driver", "C:\\Program Files (x86)\\Selenium\\chromedriver.exe");

        // initializing a Selenium WebDriver ChromeDriver instance
        WebDriver registrationDriver = new ChromeDriver();

        // connecting to the registration page
        registrationDriver.get("http://localhost/registration-page");

        // filling out the registration form
        WebElement nameInput = registrationDriver.findElement(By.id("name"));
        nameInput.sendKeys("Maria Williams");

        WebElement emailInput = registrationDriver.findElement(By.id("email"));
        // replace this with a real email address
        emailInput.sendKeys("your-address@your-domain.com");

        WebElement phoneNumberInput = registrationDriver.findElement(By.id("phone-number"));
        // replace this with your Twilio phone number
        phoneNumberInput.sendKeys("+1 555 2368");

        String password = "KW464#6r6tGaDL33Ngo*";
        WebElement passwordInput = registrationDriver.findElement(By.id("password"));
        passwordInput.sendKeys(password);

        WebElement repeatPasswordInput = registrationDriver.findElement(By.id("re_password"));
        repeatPasswordInput.sendKeys(password);

        WebElement submitButton = registrationDriver.findElement(By.id("signup"));
        // submitting the form
        submitButton.click();
    }
}

This logic works with the aforementioned registration form, but you should adapt it based on the layout of your target registration page. Also, make sure to replace +1 555 2368 with the Twilio phone retrieved earlier. Otherwise, you will not be able to use the Twilio APIs to retrieve SMS messages from it.

3. Retrieving SMS messages using Twilio

You can programmatically retrieve the last SMS messages received to your Twilio phone number with the following logic:

import com.twilio.Twilio;
import com.twilio.rest.api.v2010.account.Message;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
import java.util.concurrent.TimeUnit;

public class Main {
    public static void main(String[] args) {
        // info retrieved from the Twilio console
        final String TWILIO_ACCOUNT_SID = "<YOUR_TWILIO_ACCOUNT_SID>";
        final String TWILIO_AUTH_TOKEN = "<YOUR_TWILIO_AUTH_TOKEN>";
        final String TWILIO_PHONE_NUMBER = "<YOUR_TWILIO_PHONE_NUMBER>";

        // the phone number you expect to receive the SMS message from
        final String EXPECTED_PHONE_NUMBER = "+1 555 4598";

        // initializing Twilio
        Twilio.init(TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN);

        // iterating until verificationCode is retrieved or
        // ten iterations have been performed
        while (verificationCode == null && i < 10) {
            // retrieving the last 5 SMS messages received in the past 10 minutes
            ResourceSet<Message> lastTenSMSMessages = Message
                    .reader()
                    .setDateSentBefore(ZonedDateTime.now().minus(10, ChronoUnit.MINUTES))
                    .limit(5)
                    .read();

            // checking if one of the SMS contains the
            // verification code and extracting it...

            // waiting for 10 seconds
            // this is important because SMS messages can take longer to arrive
            TimeUnit.SECONDS.sleep(10);

            i++;
        }
    }
}

Make sure to replace <YOUR_TWILIO_ACCOUNT_SID>, <YOUR_TWILIO_AUTH_TOKEN>, and <YOUR_TWILIO_PHONE_NUMBER> with the Account SID, Auth Token, and phone number retrieved earlier from your Twilio Console page. Then, replace EXPECTED_PHONE_NUMBER with the actual phone number you expect to receive the confirmation SMS from.

Considering that SMS can take up to minutes to arrive, you should implement polling logic. Specifically, the logic above tries to recover the desired SMS several times in a while loop, waiting 10 seconds between each attempt. This should be enough to ensure that the SMS you are waiting for will be received.

In detail, the SMS message retrieval logic is based on the Message class. This Twilio static class allows you to send or receive SMS messages with your Twilio phone number. In the snippet above, Message was used to retrieve the last 5 SMS messages received in the past 10 minutes. Note that looking only for the last SMS received should not be a good approach, especially if your Twilio phone number is used by other applications.

4. Extracting the confirmation code from the SMS message

This is what the verification SMS looks like:

FooService: 808-231 is your phone number verification code to complete your registration. Do not share your code with anyone.

You can extract the verification code from the confirmation SMS body string as follows:

// checking if one of the SMS contains the
// verification code and extracting it
for (Message smsMessage : lastTenSMSMessages) {
    // if the SMS was received from the expected phone number
    // and is the confirmation SMS
    if (
            smsMessage.getFrom().toString().equals(EXPECTED_PHONE_NUMBER)
                    && smsMessage.getBody().contains("FooService:")
    ) {
        String smsBodyString = smsMessage.getBody();
        verificationCode = smsBodyString.substring(smsBodyString.lastIndexOf("FooService:"), smsBodyString.indexOf("is your phone")).trim();
    }
}

Specifically, the verification code can be extracted from the SMS message getting the text contained between “FooService:” and “is your phone” with the substring(int beginIndex, int endIndex) String method.

Putting it all together

This is what the full Selenium Java program looks like:

import com.twilio.base.ResourceSet;
import org.openqa.selenium.*;
import org.openqa.selenium.chrome.ChromeDriver;
import com.twilio.Twilio;
import com.twilio.rest.api.v2010.account.Message;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
import java.util.concurrent.TimeUnit;

public class Main {
    public static void main(String[] args) throws InterruptedException {
        // setting the system property for the Chrome Driver
        System.setProperty("webdriver.chrome.driver", "C:\\Program Files (x86)\\Selenium\\chromedriver.exe");

        // info retrieved from the Twilio console
        final String TWILIO_ACCOUNT_SID = System.getenv("<YOUR_TWILIO_ACCOUNT_SID>");
        final String TWILIO_AUTH_TOKEN = System.getenv("<YOUR_TWILIO_AUTH_TOKEN>");
        final String TWILIO_PHONE_NUMBER = "<YOUR_TWILIO_PHONE_NUMBER>";

        // the phone number you expect to receive the SMS message from
        final String EXPECTED_PHONE_NUMBER = "+1 555 4598";

        // initializing Twilio
        Twilio.init(TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN);

        // initializing a Selenium WebDriver ChromeDriver instance
        WebDriver registrationDriver = new ChromeDriver();

        // connecting to the registration page
        registrationDriver.get("http://localhost/registration-page");

        // filling out the registration form
        WebElement nameInput = registrationDriver.findElement(By.id("name"));
        nameInput.sendKeys("Maria Williams");

        WebElement emailInput = registrationDriver.findElement(By.id("email"));
        // replace this with a real email address
        emailInput.sendKeys("your-address@your-domain.com");

        WebElement phoneNumberInput = registrationDriver.findElement(By.id("phone-number"));
        // replace this with your Twilio phone number
        phoneNumberInput.sendKeys(TWILIO_PHONE_NUMBER);

        String password = "KW464#6r6tGaDL33Ngo*";
        WebElement passwordInput = registrationDriver.findElement(By.id("password"));
        passwordInput.sendKeys(password);

        WebElement repeatPasswordInput = registrationDriver.findElement(By.id("re_password"));
        repeatPasswordInput.sendKeys(password);

        WebElement submitButton = registrationDriver.findElement(By.id("signup"));
        // submitting the form
        submitButton.click();

        String verificationCode = null;
        int i = 0;

        // iterating until verificationCode is retrieved or
        // ten iterations have been performed
        while (verificationCode == null && i < 10) {
            // retrieving the last 5 SMS messages received in the past 10 minutes
            ResourceSet<Message> lastTenSMSMessages = Message
                    .reader()
                    .setDateSentBefore(ZonedDateTime.now().minus(10, ChronoUnit.MINUTES))
                    .limit(5)
                    .read();

            // checking if one of the SMS contains the
            // verification code and extracting it
            for (Message smsMessage : lastTenSMSMessages) {
                // if the SMS was received from the expected phone number
                // and is the confirmation SMS
                if (
                        smsMessage.getFrom().toString().equals(EXPECTED_PHONE_NUMBER)
                                && smsMessage.getBody().contains("FooService:")
                ) {
                    String smsBodyString = smsMessage.getBody();
                    verificationCode = smsBodyString.substring(smsBodyString.lastIndexOf("FooService:"), smsBodyString.indexOf("is your phone")).trim();
                }
            }

            // waiting for 10 seconds
            // this is important because SMS messages can take longer to arrive
            TimeUnit.SECONDS.sleep(10);

            i++;
        }

        // if the confirmation SMS did not arrive, then verificationCode would be null
        // and a NullPointerException will be thrown to make the test fail
        throw new NullPointerException("verificationCode cannot be null! SMS verification code retrieval failed!");

        System.out.println(verificationCode);

        // using the extracted verification code to verify if
        // it matches the expected one, for example by completing the
        // registration process...
    }
}

With the confirmation SMS seen before, this script would return:

808-231

You just retrieved the verification code received via SMS the newly registered user must use to confirm their phone number. Congrats!

If you’re interested in learning how to get automated test coverage for emails, you can find out more in our article on how to test email workflows with Selenium WebDriver.

Conclusion

In this article, you learned what SMS workflows are, why testing them is so important, and what the most common scenarios are when it comes to automated SMS messages. Taking into account how common SMS workflows have become, you need to know how to test them. Here, you learned how to implement a Selenium WebDriver test that triggers an SMS workflow to verify the phone number of a user who has just subscribed to a website. Then, you saw how to use Selenium to retrieve the verification code from the confirmation SMS sent to the phone number of the user and verify if it matches the expected code.

Thanks for reading! We hope that you found this article helpful. Feel free to reach out to us on Twitter with any questions, comments, or suggestions.

Get started with Reflect today

Create your first test in 2 minutes, no installation or setup required. Accelerate your testing efforts with fast and maintainable test suites without writing a line of code.

Copyright © 2022 Reflect Software Inc. All Rights Reserved.