Skip to main content

Selenium with JUnit 5

Learn how to run your first Selenium Webdriver test with JUnit 5 on BrowserStack Automate.

Important: Sample test scripts are available in the junit-browserstack repository.

The sample test script in this section is compatible with JSON wire protocol-based client bindings. Check out our W3C-based scripts in the selenium-4 branch of the repository.

Introduction

BrowserStack gives you instant access to our Selenium Grid of 3000+ real devices and desktop browsers. Running your Selenium tests with JUnit 5 on BrowserStack is simple.

This guide will help you:

  1. Run your first test
  2. Understand your tests with BrowserStack
  3. Mark tests as passed or failed
  4. Debug your app

Prerequisites

  • BrowserStack Username and Access key, which you can find in your account settings. If you have not created an account yet, you can sign up for a Free Trial or purchase a plan.
  • Maven is installed on your machine, Maven environment variables are set, and Maven bin is added to system path, $PATH. Check out the official website to download the latest version of Maven.
  • Git installed on your machine.

Run your first test

To run Selenium tests with JUnit 5 on BrowserSatck Automate, complete the following steps:

Step 1: Clone the junit-browserstack repository using the following command:

    git clone https://github.com/browserstack/junit-browserstack.git

Step 2: Run the following command in your command-line to install the required dependencies:

    cd junit-browserstack/junit-5
    mvn clean install

Step 3: Set your BrowserStack credentials in the caps.json file in the junit-5/src/test/resources/ directory as follows:

junit-5/src/test/resources/caps.json
    {
       "server":"hub.browserstack.com",
       "user":"YOUR_USERNAME",
       "key":"YOUR_ACCESS_KEY",
       "tests":{
          "parallel": {
            "common_caps": {
              "name": "parallel-test",
              "build": "junit5-browserstack",
              "browserstack.console": "verbose",
              "browserstack.debug": "true"
            },
            "platform": {
              "chrome": {
                "browser": "chrome",
                "browser_version": "latest-1",
                "os": "Windows",
                "os_version": "10"
              },
              "safari": {
                "browser": "safari",
                "browser_version": "latest",
                "os": "OS X",
                "os_version": "Big Sur"
              },
              "firefox": {
                "browser": "firefox",
                "browser_version": "latest-2",
                "os": "OS X",
                "os_version": "Monterey"
              },
              "edge": {
                "browser": "edge",
                "browser_version": "latest",
                "os": "Windows",
                "os_version": "11"
              },
              "android": {
                "device": "Samsung Galaxy S21",
                "os_version": "11.0"
              },
              "ios": {
                "device": "iPhone 12 Mini",
                "os_version": "14"
              },
              "tab": {
                "device": "Samsung Galaxy Tab S7"
              },
              "ipad": {
                "device": "iPad Mini 2019"
              },
              "xiaomi": {
                "device": "Xiaomi Redmi Note 9"
              },
              "oneplus": {
                "device": "OnePlus 9"
              }
            }
       }
    }

Alternatively, you can set the environment variables in your system as shown below:

# setx.exe does not set the environment variable in the current command prompt, but it will be available in subsequent command prompts
setx BROWSERSTACK_USERNAME="YOUR_USERNAME"
setx BROWSERSTACK_ACCESS_KEY="YOUR_ACCESS_KEY"

# Verify whether the variables have been set
echo %BROWSERSTACK_USERNAME%
echo %BROWSERSTACK_ACCESS_KEY%
# Set these values in your ~/.zprofile (zsh) or ~/.profile (bash)
export BROWSERSTACK_USERNAME="YOUR_USERNAME"
export BROWSERSTACK_ACCESS_KEY="YOUR_ACCESS_KEY"

# Verify whether the variables have been set
echo $BROWSERSTACK_USERNAME
echo $BROWSERSTACK_ACCESS_KEY

Retrieve the value in your script using the following syntax:

  String username = System.getenv("BROWSERSTACK_USERNAME");
  String accessKey = System.getenv("BROWSERSTACK_ACCESS_KEY");

Step 4: Verify the following configuration parameters that enable parallel execution are set in the pom.xml file.

junit-browserstack/junit-5/pom.xml
<properties>
    <configurationParameters>
        junit.jupiter.execution.parallel.enabled = true
        junit.jupiter.execution.parallel.mode.default = concurrent
        junit.jupiter.execution.parallel.config.strategy=fixed
        junit.jupiter.execution.parallel.config.fixed.parallelism=<parallel-count-value>
    </configurationParameters>
</properties>

Step 5: Run a single test using the following command:

    mvn test -P parallel

Step 6: Navigate to your Automate Dashboard and view the tests running in parallel on multiple browsers and devices.

Understand the details of your test

Learn about how the different components of the test suite work together using the following information.

Understand your first test script

When you run the mvn test -P parallel command, the SingleTest.java file within the junit-browserstack/junit-5/src/test/java/tests directory is executed on several browsers and devices as defined in the JUnit 5 configuration file.

When the test is triggered, it:

  • Opens the bstackdemo.com website.
  • Adds a product to the cart.
  • Verifies whether the product is added to the cart.
  • Marks the test as passed or failed based on whether the product is available in the cart.
junit-browserstack/junit-5/src/test/java/tests/SingleTest.java
public class SingleTest {

  @WebDriverTest
  void singleTest(WebDriver driver) {
    SessionId sessionId = ((RemoteWebDriver) driver).getSessionId();
    MarkSessionStatus sessionStatus = new MarkSessionStatus(sessionId);

    try {
      driver.get("https://bstackdemo.com/");
      final WebDriverWait wait = new WebDriverWait(driver, 10);
      wait.until(ExpectedConditions.titleIs("StackDemo"));
      String product_name = wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("//*[@id='1']/p"))).getText();
      WebElement cart_btn = wait.until(ExpectedConditions.elementToBeClickable(By.xpath("//*[@id='1']/div[4]")));
      cart_btn.click();
      wait.until(ExpectedConditions.visibilityOfElementLocated(By.className("float-cart__content")));
      final String product_in_cart = wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("//*[@id='__next']/div/div/div[2]/div[2]/div[2]/div/div[3]/p[1]"))).getText();
      if (product_name.equals(product_in_cart)) {
        sessionStatus.markTestStatus("passed", "Product has been successfully added to the cart!");
      } else {
        sessionStatus.markTestStatus("failed", "There was some issue!");
      }
    } catch (Exception e) {
      sessionStatus.markTestStatus("failed", "There was some issue!");
      System.out.println("Exception: " + e.getMessage());
    }
    driver.quit();
  }
}

@WebDriverTest is the interface that defines Test Template for the Junit 5 tests and extends the BstackRunner class.

junit-browserstack/junit-5/src/test/java/runners/WebDriverTest.java
import org.apiguardian.api.API;
import org.junit.jupiter.api.TestTemplate;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.platform.commons.annotation.Testable;

import java.lang.annotation.*;

@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@API(status = API.Status.STABLE)
@TestTemplate
@Testable
@ExtendWith(BstackRunner.class)
public @interface WebDriverTest {
}

Understand the JUnit 5 configuration file

In the sample repository, the configuration file named caps.json exists in the junit-browserstack/junit-5/src/test/resources directory of the project. It includes the default configuration for connecting tests to BrowserStack, and sample capabilities, such as browser name and its version, for running tests.

caps.json
  {
  "server": "hub.browserstack.com",
  "user": "BROWSERSTACK_USERNAME",
  "key": "BROWSERSTACK_ACCESS_KEY",
  "tests": {
    "single": {
      "common_caps": {
        "name": "single-test",
        "build": "junit5-browserstack",
        "browserstack.console": "verbose",
        "browserstack.networkLogs": "true",
        "browserstack.debug": "true"
      },
      "platform": {
        "chrome": {
          "browser": "chrome",
          "browser_version": "latest",
          "os": "Windows",
          "os_version": "10"
        }
      }
    },
    "local": {
      "common_caps": {
        "name": "local-test",
        "build": "junit5-browserstack",
        "browserstack.console": "verbose",
        "browserstack.local": "true",
        "browserstack.debug": "true"
      },
      "platform": {
        "chrome": {
          "browser": "chrome",
          "browser_version": "latest",
          "os": "Windows",
          "os_version": "10"
        }
      }
    },
    "parallel": {
      "common_caps": {
        "name": "parallel-test",
        "build": "junit5-browserstack",
        "browserstack.console": "verbose",
        "browserstack.debug": "true"
      },
      "platform": {
        "chrome": {
          "browser": "chrome",
          "browser_version": "latest-1",
          "os": "Windows",
          "os_version": "10"
        },
        "safari": {
          "browser": "safari",
          "browser_version": "latest",
          "os": "OS X",
          "os_version": "Big Sur"
        },
        "firefox": {
          "browser": "firefox",
          "browser_version": "latest-2",
          "os": "OS X",
          "os_version": "Monterey"
        },
        "edge": {
          "browser": "edge",
          "browser_version": "latest",
          "os": "Windows",
          "os_version": "11"
        },
        "android": {
          "device": "Samsung Galaxy S21",
          "os_version": "11.0"
        },
        "ios": {
          "device": "iPhone 12 Mini",
          "os_version": "14"
        },
        "tab": {
          "device": "Samsung Galaxy Tab S7"
        },
        "ipad": {
          "device": "iPad Mini 2019"
        },
        "xiaomi": {
          "device": "Xiaomi Redmi Note 9"
        },
        "oneplus": {
          "device": "OnePlus 9"
        }
      }
    }
  }
}

Integrate your tests with BrowserStack

The BstackRunner.java file in the junit-browserstack/junit-5/src/test/java/runners directory helps in running the integration of your test with BrowserStack. It contains the methods to configure and create the connection with BrowserStack as follows:

junit-browserstack/junit-5/src/test/java/runners/BstackRunner.java
public class BstackRunner implements TestTemplateInvocationContextProvider {
  public WebDriver driver;
  public DesiredCapabilities capabilities;
  public String username, accesskey, server;
  private JSONObject mainConfig;
  private JSONObject browserConfig;
  private JSONObject profileConfig;
  private JSONObject testConfig;
  private JSONObject platformConfig;
  private JSONObject commonCapsConfig;
  private HashMap<String, String> allCapsMap;
  private HashMap<String, String> commonCapsMap;

  public BstackRunner() {
    this.username = setupCredsAndServer().get("username");
    this.accesskey = setupCredsAndServer().get("accesskey");
    this.server = setupCredsAndServer().get("server");
  }

  public HashMap<String, String> setupCredsAndServer() {
    try {
      JSONParser parse = new JSONParser();
      mainConfig = (JSONObject) parse.parse(new FileReader("src/test/resources/caps.json"));
      server = (String) mainConfig.get("server");
      username = System.getenv("BROWSERSTACK_USERNAME");
      if (username == null) {
        username = (String) mainConfig.get("user");
      }
      accesskey = System.getenv("BROWSERSTACK_ACCESS_KEY");
      if (accesskey == null) {
        accesskey = (String) mainConfig.get("key");
      }
    } catch (Exception e) {
      System.out.println(e.getMessage());
    }
    HashMap<String, String> creds = new HashMap();
    creds.put("username", username);
    creds.put("accesskey", accesskey);
    creds.put("server", server);
    return creds;
  }

  @Override
  public boolean supportsTestTemplate(ExtensionContext extensionContext) {
    return true;
  }

  @Override
  public Stream<TestTemplateInvocationContext> provideTestTemplateInvocationContexts(ExtensionContext extensionContext) {
    List<TestTemplateInvocationContext> desiredCapsInvocationContexts = new ArrayList<>();
    //picks the test profile based on the maven command executed - single, local, parallel
    String profile = System.getProperty("config");

    try {
      testConfig = (JSONObject) mainConfig.get("tests");
      profileConfig = (JSONObject) testConfig.get(profile);
      platformConfig = (JSONObject) profileConfig.get("platform");
      commonCapsConfig = (JSONObject) profileConfig.get("common_caps");
      commonCapsMap = (HashMap<String, String>) commonCapsConfig;
      Iterator platformIterator = platformConfig.keySet().iterator();

      while (platformIterator.hasNext()) {
        capabilities = new DesiredCapabilities();
        Iterator commonCapsIterator = commonCapsMap.entrySet().iterator();
        while (commonCapsIterator.hasNext()) {
          Map.Entry capsName = (Map.Entry) commonCapsIterator.next();
          capabilities.setCapability((String) capsName.getKey(), capsName.getValue());
        }
        final String platformName = (String) platformIterator.next();
        browserConfig = (JSONObject) platformConfig.get(platformName);
        allCapsMap = (HashMap<String, String>) browserConfig;
        Iterator finalCapsIterator = allCapsMap.entrySet().iterator();
        while (finalCapsIterator.hasNext()) {
          Map.Entry pair = (Map.Entry) finalCapsIterator.next();
          capabilities.setCapability((String) pair.getKey(), pair.getValue());
        }
        //Initializing local testing connection
        if (capabilities.getCapability("browserstack.local") != null && capabilities.getCapability("browserstack.local").toString().equals("true")) {
          HashMap<String, String> localOptions = new HashMap<>();
          localOptions.put("key", accesskey);
          //Add more local options here, e.g. forceLocal, localIdentifier, etc.
          SetupLocalTesting.createInstance(localOptions);
        }
        desiredCapsInvocationContexts.add(invocationContext(capabilities));

      }
    } catch (Exception e) {
      System.out.println(e);
    }
    return desiredCapsInvocationContexts.stream();
  }

  private TestTemplateInvocationContext invocationContext(DesiredCapabilities caps) {
    return new TestTemplateInvocationContext() {

      @Override
      public List<Extension> getAdditionalExtensions() {

        return Collections.singletonList(new ParameterResolver() {
          @Override
          public boolean supportsParameter(ParameterContext parameterContext,
                                           ExtensionContext extensionContext) {
            return parameterContext.getParameter().getType().equals(WebDriver.class);
          }

          @Override
          public Object resolveParameter(ParameterContext parameterContext,
                                         ExtensionContext extensionContext) {
            try {
              driver = new RemoteWebDriver(new URL("https://" + username + ":" + accesskey + "@" + server + "/wd/hub"), caps);
            } catch (MalformedURLException e) {
              e.printStackTrace();
            }
            return driver;
          }
        });
      }
    };
  }
}

Understand the dependencies

The required dependencies to run all the tests in the repository are added to the pom.xml file as shown below:

pom.xml
    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>Junit5Basics</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <junit.jupiter>5.8.1</junit.jupiter>
        <junit-vintage-engine>5.4.0</junit-vintage-engine>
        <json-simple>1.1</json-simple>
        <selenium-java>3.141.59</selenium-java>
        <maven-surefire-plugin>3.0.0-M5</maven-surefire-plugin>
        <browserstack-local-java>1.0.6</browserstack-local-java>

        <parallel.count>5</parallel.count>
        <tests.single>**/tests.SingleTest.java</tests.single>
        <tests.local>**/tests.LocalTest.java</tests.local>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-engine</artifactId>
            <version>${junit.jupiter}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.vintage</groupId>
            <artifactId>junit-vintage-engine</artifactId>
            <version>${junit-vintage-engine}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.googlecode.json-simple</groupId>
            <artifactId>json-simple</artifactId>
            <version>${json-simple}</version>
        </dependency>
        <dependency>
            <groupId>org.seleniumhq.selenium</groupId>
            <artifactId>selenium-java</artifactId>
            <version>${selenium-java}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-surefire-plugin</artifactId>
            <version>${maven-surefire-plugin}</version>
            <type>maven-plugin</type>
        </dependency>
        <dependency>
            <groupId>com.browserstack</groupId>
            <artifactId>browserstack-local-java</artifactId>
            <version>${browserstack-local-java}</version>
        </dependency>
    </dependencies>

    <profiles>
        <profile>
            <id>single</id>
            <build>
                <plugins>
                    <plugin>
                        <groupId>org.apache.maven.plugins</groupId>
                        <artifactId>maven-surefire-plugin</artifactId>
                        <version>3.0.0-M5</version>
                        <configuration>
                            <includes>
                                <include>${tests.single}</include>
                            </includes>
                            <systemPropertyVariables>
                                <config>single</config>
                            </systemPropertyVariables>
                            <testFailureIgnore>false</testFailureIgnore>
                        </configuration>
                    </plugin>
                </plugins>
            </build>
        </profile>
        <profile>
            <id>local</id>
            <build>
                <plugins>
                    <plugin>
                        <groupId>org.apache.maven.plugins</groupId>
                        <artifactId>maven-surefire-plugin</artifactId>
                        <version>3.0.0-M5</version>
                        <configuration>
                            <includes>
                                <include>${tests.local}</include>
                            </includes>
                            <systemPropertyVariables>
                                <config>local</config>
                            </systemPropertyVariables>
                            <testFailureIgnore>false</testFailureIgnore>
                        </configuration>
                    </plugin>
                </plugins>
            </build>
        </profile>
        <profile>
            <id>parallel</id>
            <build>
                <plugins>
                    <plugin>
                        <groupId>org.apache.maven.plugins</groupId>
                        <artifactId>maven-surefire-plugin</artifactId>
                        <version>3.0.0-M5</version>
                        <configuration>
                            <includes>
                                <include>${tests.single}</include>
                            </includes>
                            <systemPropertyVariables>
                                <config>parallel</config>
                            </systemPropertyVariables>
                            <properties>
                                <configurationParameters>
                                    junit.jupiter.execution.parallel.enabled = true
                                    junit.jupiter.execution.parallel.mode.default = concurrent
                                    junit.jupiter.execution.parallel.config.strategy=fixed
                                    junit.jupiter.execution.parallel.config.fixed.parallelism=${parallel.count}
                                </configurationParameters>
                            </properties>
                            <testFailureIgnore>false</testFailureIgnore>
                        </configuration>
                    </plugin>
                </plugins>
            </build>
        </profile>
    </profiles>
    </project>

Mark tests as passed or failed

BrowserStack does not know whether the assertions in your tests have passed or failed because only the test script knows whether the assertions have passed. Therefore, based on the assertions on your script, you have to explicitly inform BrowserStack whether your tests have passed or not using information in this section.

It is possible to mark tests as either a pass or a fail and also state a reason using the following sample code snippet:

junit-browserstack/junit-5/src/test/java/utils/MarkSessionStatus.java
// Mark test as pass / fail
public class MarkSessionStatus extends BstackRunner {
  SessionId sessionId;

  public MarkSessionStatus(SessionId sessionId) {
    this.sessionId = sessionId;
  }

  public void markTestStatus(String status, String reason) {
    try {
      URI uri = new URI("https://" + username + ":" + accesskey + "@api.browserstack.com/automate/sessions/" + sessionId + ".json");
      HttpPut putRequest = new HttpPut(uri);

      ArrayList<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();
      nameValuePairs.add((new BasicNameValuePair("status", status)));
      nameValuePairs.add((new BasicNameValuePair("reason", reason)));
      putRequest.setEntity(new UrlEncodedFormEntity(nameValuePairs));

      HttpClientBuilder.create().build().execute(putRequest);
    } catch (Exception e) {
      System.out.println("Exception: " + e.getMessage());
    }
  }
}

You can find the full reference to our REST API.

Debug your app

BrowserStack provides a range of debugging tools to help you quickly identify and fix bugs you discover through your automated tests.

Text logs

Text Logs are a comprehensive record of your test. They are used to identify all the steps executed in the test and troubleshoot errors for the failed step. Text Logs are accessible from the Automate dashboard or via our REST API.

Visual logs

Visual Logs automatically capture the screenshots generated at every Selenium command run through your JUnit 5 tests. Visual Logs help debug the exact step and the page where the failure occurred. They also help identify any layout or design-related issues with your web pages on different browsers.

Visual Logs are disabled by default. To enable Visual Logs, set the browserstack.debug capability to true.

"common_caps": {
  "browserstack.debug": "true"
}

Sample Visual Logs from Automate Dashboard: BrowserStack Automate Visual Logs

Video recording

Every test run on the BrowserStack Selenium grid is recorded as executed on our remote machine. This feature is beneficial whenever a browser test fails. You can access videos from Automate Dashboard for each session. You can also download the videos from the Dashboard or retrieve a link to download the video using our REST API.

Note: Video recording increases test execution time slightly. You can disable this feature by setting the browserstack.video capability to false.

Note: Video recording increases test execution time slightly. You can disable this feature by setting the browserstack.video capability to false.
"common_caps": {
  "browserstack.video": false
}

In addition to these logs BrowserStack also provides Raw Logs, Network Logs, Console Logs, Selenium Logs, Appium Logs, and Interactive sessions. You can find the complete details to enable all the debugging options.

Next steps

After you have successfully run your first test on BrowserStack, you might want to do one of the following:

We're sorry to hear that. Please share your feedback so we can do better

Contact our Support team for immediate help while we work on improving our docs.

We're continuously improving our docs. We'd love to know what you liked






Thank you for your valuable feedback

Is this page helping you?

Yes
No

We're sorry to hear that. Please share your feedback so we can do better

Contact our Support team for immediate help while we work on improving our docs.

We're continuously improving our docs. We'd love to know what you liked






Thank you for your valuable feedback!

Talk to an Expert
Talk to an Expert