Skip to main content
Back to blog

Run Selenium Tests in Azure DevOps Pipeline: Step-by-Step YAML

Complete guide to running Selenium tests in Azure DevOps pipelines. Covers Java and Python examples, ChromeDriver setup on Linux agents, headless browser configuration, test result publishing, and parallel execution.

InnovateBits4 min read
Share

Running Selenium tests in Azure DevOps requires configuring headless browsers on the agent, setting up the correct WebDriver, and publishing test results in a format Azure DevOps understands. This guide covers complete working YAML for both Java (TestNG/JUnit) and Python (pytest) setups.


Prerequisites on the agent

Microsoft-hosted Ubuntu agents include:

  • Chrome browser (regularly updated)
  • ChromeDriver matching the Chrome version
  • Java 17
  • Python 3.10+
  • Maven, Gradle

They do NOT include a display server, so all browser tests must run in headless mode.


Java + TestNG pipeline

Project structure

selenium-tests/
  pom.xml
  src/test/java/
    tests/
      LoginTest.java
      CheckoutTest.java
  testng.xml

pom.xml dependencies

<dependencies>
  <dependency>
    <groupId>org.seleniumhq.selenium</groupId>
    <artifactId>selenium-java</artifactId>
    <version>4.21.0</version>
  </dependency>
  <dependency>
    <groupId>org.testng</groupId>
    <artifactId>testng</artifactId>
    <version>7.10.2</version>
    <scope>test</scope>
  </dependency>
  <dependency>
    <groupId>io.github.bonigarcia</groupId>
    <artifactId>webdrivermanager</artifactId>
    <version>5.8.0</version>
    <scope>test</scope>
  </dependency>
</dependencies>

Headless Chrome setup in tests

// BaseTest.java
import io.github.bonigarcia.wdm.WebDriverManager;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
 
public class BaseTest {
    protected WebDriver driver;
 
    @BeforeMethod
    public void setUp() {
        WebDriverManager.chromedriver().setup();
 
        ChromeOptions options = new ChromeOptions();
        options.addArguments("--headless=new");   // Headless mode for CI
        options.addArguments("--no-sandbox");       // Required in Docker/CI
        options.addArguments("--disable-dev-shm-usage"); // Prevent memory issues
        options.addArguments("--window-size=1920,1080");
 
        driver = new ChromeDriver(options);
    }
 
    @AfterMethod
    public void tearDown() {
        if (driver != null) driver.quit();
    }
}

Azure Pipelines YAML — Java

trigger:
  branches:
    include: [main]
 
pool:
  vmImage: ubuntu-latest
 
stages:
  - stage: SeleniumTests
    displayName: Selenium Tests
    jobs:
      - job: TestNG
        displayName: TestNG Selenium Suite
        timeoutInMinutes: 30
        steps:
          - task: JavaToolInstaller@0
            displayName: Setup Java 17
            inputs:
              versionSpec: '17'
              jdkArchitectureOption: x64
              jdkSourceOption: PreInstalled
 
          - script: mvn --version && java --version
            displayName: Verify Java setup
 
          - script: |
              mvn test \
                -Dsurefire.rerunFailingTestsCount=1 \
                -DBASE_URL=$(STAGING_URL)
            displayName: Run Selenium tests
            env:
              STAGING_URL: $(STAGING_URL)
 
          - task: PublishTestResults@2
            displayName: Publish TestNG results
            inputs:
              testResultsFormat: JUnit
              testResultsFiles: '**/surefire-reports/TEST-*.xml'
              testRunTitle: Selenium TestNG — $(Build.BuildNumber)
              mergeTestResults: true
            condition: always()
 
          - task: PublishPipelineArtifact@1
            displayName: Upload screenshots on failure
            inputs:
              targetPath: test-screenshots
              artifact: selenium-screenshots
            condition: failed()

Python + pytest pipeline

Requirements

# requirements.txt
selenium==4.21.0
pytest==8.2.0
pytest-html==4.1.1
pytest-xdist==3.5.0   # For parallel execution

Headless Chrome in Python

# conftest.py
import pytest
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.chrome.service import Service
 
@pytest.fixture(scope="function")
def driver():
    options = Options()
    options.add_argument("--headless=new")
    options.add_argument("--no-sandbox")
    options.add_argument("--disable-dev-shm-usage")
    options.add_argument("--window-size=1920,1080")
 
    service = Service()  # WebDriverManager or system ChromeDriver
    browser = webdriver.Chrome(service=service, options=options)
    yield browser
    browser.quit()

Azure Pipelines YAML — Python

trigger:
  branches:
    include: [main]
 
pool:
  vmImage: ubuntu-latest
 
stages:
  - stage: SeleniumTests
    jobs:
      - job: Pytest
        timeoutInMinutes: 30
        steps:
          - task: UsePythonVersion@0
            inputs:
              versionSpec: '3.11'
 
          - script: pip install -r requirements.txt
            displayName: Install dependencies
 
          - script: |
              pytest tests/ \
                --junitxml=test-results/results.xml \
                --html=test-results/report.html \
                --self-contained-html \
                -n auto \
                -v
            displayName: Run Selenium tests
            env:
              BASE_URL: $(STAGING_URL)
 
          - task: PublishTestResults@2
            displayName: Publish pytest results
            inputs:
              testResultsFormat: JUnit
              testResultsFiles: test-results/results.xml
              testRunTitle: Selenium pytest — $(Build.BuildNumber)
            condition: always()
 
          - task: PublishPipelineArtifact@1
            inputs:
              targetPath: test-results/report.html
              artifact: pytest-html-report
            condition: always()

Common errors and fixes

Error: DevToolsActivePort file doesn't exist in Chrome Fix: Add --no-sandbox and --disable-dev-shm-usage Chrome options. This error occurs in Linux environments without proper Chrome sandbox support.

Error: ChromeDriver only supports Chrome version 114 — version mismatch Fix: Use WebDriverManager (io.github.bonigarcia:webdrivermanager) in Java or webdriver-manager in Python — it automatically downloads the matching ChromeDriver version.

Error: Tests pass locally but fail in pipeline with "element not visible" Fix: Headless Chrome may render differently. Set explicit window size (--window-size=1920,1080) and add explicit waits (WebDriverWait) instead of Thread.sleep().

Error: Screenshots directory not found when trying to upload artifact Fix: Create the directory unconditionally: - script: mkdir -p test-screenshots before tests run. Upload artifacts with condition: always() not condition: failed().

Error: Maven build fails: "Tests run: 0, Failures: 0, Errors: 0, Skipped: 0" Fix: Check that test class names match the Surefire plugin pattern (default: Test*.java, *Test.java, *Tests.java). Add <includes><include>**/*Test.java</include></includes> to Surefire plugin config if using a different pattern.

Free newsletter

Stay ahead in AI-driven QA

Get practical tutorials on test automation, AI testing, and quality engineering — straight to your inbox. No spam, unsubscribe any time.

Discussion

Sign in with GitHub to comment · powered by Giscus