Skip to main content
Back to Blog
Reference
2026-05-20

Selenium Azure DevOps Pipeline Complete Guide 2026

Master Selenium with Azure DevOps Pipelines in 2026. Cover YAML syntax, parallel jobs, Microsoft-hosted vs self-hosted agents, Grid integration, and reporting.

Selenium Azure DevOps Pipeline Complete Guide 2026

Azure DevOps Pipelines (formerly Azure Pipelines, formerly VSTS) is Microsoft's CI/CD service. For organizations standardized on the Microsoft ecosystem (Azure, .NET, Office 365, Active Directory) it integrates more cleanly than alternative CI tools. For Selenium test suites it offers managed Windows, Linux, and macOS agents, parallel job execution, native test result publishing, and tight integration with Azure Resource Manager for spinning up test infrastructure.

This guide covers Selenium + Azure DevOps Pipelines end-to-end in 2026. We walk through YAML pipeline syntax, Microsoft-hosted agents, self-hosted agent pools, parallel jobs, matrix strategies for cross-browser runs, Selenium Grid integration, publishing JUnit and Allure results, artifact handling, deployment gates, and integration with Azure Test Plans. For Selenium fundamentals see Selenium Java TestNG and for Grid see Selenium Grid 4. Browse the skills directory.

Why Azure DevOps

Three reasons. First, Microsoft integration. If your org runs on Azure, AAD, Teams, and SQL Server, Azure DevOps is the lowest-friction CI. Second, mature test reporting. Azure Test Plans plus Pipelines provides traceability from a planned test case to a CI run that executed it. Third, parallel job scaling. Free tiers include 1800 minutes/month of Microsoft-hosted agents; paid tiers scale linearly.

The trade-off is the learning curve. Azure DevOps has accumulated many concepts (organizations, projects, pipelines, releases, environments, agent pools). For first-time users this is overwhelming. Once internalized the platform is productive.

ComponentPurpose
PipelineYAML or classic definition
JobSequence of steps running on one agent
StageLogical phase containing jobs
VariablePipeline-scoped value
Variable groupOrg-scoped reusable variables
Service connectionAuth to external services
Agent poolSet of agents (hosted or self-hosted)
EnvironmentDeployment target with approval gates

Basic Pipeline

A minimal YAML pipeline for Selenium tests.

# azure-pipelines.yml
trigger:
  branches:
    include:
      - main
      - develop
  paths:
    include:
      - 'src/**'
      - 'tests/**'

pr:
  branches:
    include:
      - main

variables:
  - name: GRID_URL
    value: 'http://selenium-grid.internal:4444'
  - group: 'selenium-secrets'

pool:
  vmImage: 'ubuntu-latest'

jobs:
  - job: SeleniumTests
    displayName: 'Run Selenium Tests'
    timeoutInMinutes: 60

    steps:
      - task: Cache@2
        inputs:
          key: 'maven | "$(Agent.OS)" | **/pom.xml'
          restoreKeys: |
            maven | "$(Agent.OS)"
            maven
          path: '$(HOME)/.m2/repository'

      - task: JavaToolInstaller@0
        inputs:
          versionSpec: '17'
          jdkArchitectureOption: 'x64'
          jdkSourceOption: 'PreInstalled'

      - script: |
          mvn test -DgridUrl=$(GRID_URL) -Dbrowser=chrome
        displayName: 'Run tests'
        env:
          LOAD_TEST_PASSWORD: $(LoadTestPassword)

      - task: PublishTestResults@2
        condition: always()
        inputs:
          testResultsFormat: 'JUnit'
          testResultsFiles: '**/target/surefire-reports/TEST-*.xml'
          failTaskOnFailedTests: true

      - task: PublishBuildArtifacts@1
        condition: failed()
        inputs:
          PathtoPublish: '$(System.DefaultWorkingDirectory)/target/screenshots'
          ArtifactName: 'screenshots'

This pipeline triggers on PRs and main branch pushes, caches Maven dependencies, runs tests, publishes JUnit results, and uploads screenshots on failure.

Hosted vs Self-Hosted Agents

Microsoft-hosted agents are managed VMs that Microsoft provisions on demand. They are pre-installed with common toolchains (JDK, .NET, Node, Python). Use them for most workloads.

Self-hosted agents run on your infrastructure. Use them when you need:

  • Access to internal networks
  • Larger VMs than Microsoft offers
  • Persistent state across runs
  • Custom OS or hardware
# Microsoft-hosted agent
pool:
  vmImage: 'ubuntu-latest'  # or 'windows-latest', 'macos-latest'

# Self-hosted pool
pool:
  name: 'self-hosted-linux'
  demands:
    - selenium-grid
    - docker

demands filter which agents in the pool can run the job. Agents declare capabilities; jobs declare demands; Azure matches them.

Parallel Jobs

For cross-browser runs.

jobs:
  - job: ChromeTests
    pool: { vmImage: 'ubuntu-latest' }
    steps:
      - script: mvn test -Dbrowser=chrome

  - job: FirefoxTests
    pool: { vmImage: 'ubuntu-latest' }
    steps:
      - script: mvn test -Dbrowser=firefox

  - job: EdgeTests
    pool: { vmImage: 'windows-latest' }
    steps:
      - script: mvn test -Dbrowser=edge

Each job runs in parallel on its own agent. Total time is the slowest job.

Matrix Strategy

For more variants use matrix.

jobs:
  - job: CrossBrowser
    pool: { vmImage: 'ubuntu-latest' }
    strategy:
      matrix:
        Chrome_Stable:
          BROWSER: 'chrome'
          BROWSER_VERSION: 'stable'
        Chrome_Beta:
          BROWSER: 'chrome'
          BROWSER_VERSION: 'beta'
        Firefox_ESR:
          BROWSER: 'firefox'
          BROWSER_VERSION: 'esr'
        Firefox_Latest:
          BROWSER: 'firefox'
          BROWSER_VERSION: 'latest'
      maxParallel: 4

    steps:
      - script: |
          mvn test \
            -Dbrowser=$(BROWSER) \
            -DbrowserVersion=$(BROWSER_VERSION) \
            -DgridUrl=$(GRID_URL)
        displayName: 'Test $(BROWSER) $(BROWSER_VERSION)'

Matrix creates one job per row. maxParallel caps concurrent runs to control Grid load.

Multi-Stage Pipeline

For end-to-end pipelines with build, test, deploy stages.

stages:
  - stage: Build
    jobs:
      - job: Compile
        steps:
          - script: mvn clean compile -DskipTests
          - task: PublishPipelineArtifact@1
            inputs:
              path: '$(System.DefaultWorkingDirectory)/target'
              artifact: 'build-output'

  - stage: Test
    dependsOn: Build
    jobs:
      - job: SmokeTest
        steps:
          - task: DownloadPipelineArtifact@2
            inputs:
              artifact: 'build-output'
              path: 'target'
          - script: mvn test -Dgroups=smoke

      - job: RegressionTest
        dependsOn: SmokeTest
        steps:
          - script: mvn test -Dgroups=regression

  - stage: Deploy
    dependsOn: Test
    condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main'))
    jobs:
      - deployment: ToStaging
        environment: 'staging'
        strategy:
          runOnce:
            deploy:
              steps:
                - script: ./deploy.sh staging

Stages run sequentially by default. dependsOn controls order. condition filters when stages execute.

Selenium Grid in Pipeline

Spin up a Grid container per pipeline.

- job: SeleniumWithGrid
  pool: { vmImage: 'ubuntu-latest' }

  services:
    grid:
      image: selenium/hub:4.27.0
      ports:
        - '4444:4444'
        - '4442:4442'
        - '4443:4443'
    chrome:
      image: selenium/node-chrome:4.27.0
      env:
        SE_EVENT_BUS_HOST: grid
        SE_EVENT_BUS_PUBLISH_PORT: '4442'
        SE_EVENT_BUS_SUBSCRIBE_PORT: '4443'

  steps:
    - script: |
        until curl -sSL http://localhost:4444/wd/hub/status | jq -r '.value.ready' | grep -q true; do
          sleep 2
        done
      displayName: 'Wait for Grid'

    - script: |
        mvn test -DgridUrl=http://localhost:4444
      displayName: 'Run tests'

Service containers run alongside the job container with shared networking. The job container can reach http://grid:4444 (using the service name).

Variable Groups and Secrets

Store secrets in a variable group (or Azure Key Vault for higher security).

variables:
  - group: 'selenium-secrets'        # Set in Library
  - name: PUBLIC_VAR
    value: 'http://staging.example.com'

steps:
  - script: |
      echo "URL: $(PUBLIC_VAR)"
      mvn test -Dpassword=$(SECRET_PASSWORD)

Variables in groups marked as secrets don't appear in logs and need explicit \$(...) to use.

Publishing Test Results

Azure DevOps natively renders JUnit XML.

- task: PublishTestResults@2
  condition: always()
  inputs:
    testResultsFormat: 'JUnit'
    testResultsFiles: '**/target/surefire-reports/TEST-*.xml'
    testRunTitle: 'Selenium - $(BROWSER) - $(Build.BuildNumber)'
    failTaskOnFailedTests: true

Results appear in the Tests tab. You see pass/fail counts, duration trends, and flaky test detection automatically.

For Allure reports use a custom step that copies allure-results to an artifact:

- script: |
    allure generate target/allure-results -o target/allure-report --clean
  condition: always()
  displayName: 'Generate Allure report'

- task: PublishHtmlReport@1
  condition: always()
  inputs:
    reportDir: 'target/allure-report'
    tabName: 'Allure'

The HTML Report extension renders Allure inline in the pipeline run.

Deployment Gates

For deployment stages add approval gates.

- deployment: ToProduction
  environment:
    name: 'production'
    resourceType: 'Kubernetes'
  strategy:
    runOnce:
      preDeploy:
        steps:
          - script: ./smoke-test.sh production
      deploy:
        steps:
          - script: ./deploy.sh production
      routeTraffic:
        steps:
          - script: ./route-traffic.sh
      postRouteTraffic:
        steps:
          - script: ./smoke-test.sh production

Environments configured with approval gates pause the pipeline until a designated approver clicks Approve.

Azure Test Plans Integration

For organizations using Azure Test Plans, pipelines can publish test results that map back to planned test cases. This gives manual and automated tests a unified report.

- task: PublishTestResults@2
  inputs:
    testResultsFormat: 'JUnit'
    testResultsFiles: '**/target/surefire-reports/*.xml'
    publishRunAttachments: true
    mergeTestResults: true
    testRunTitle: 'Selenium Run $(Build.BuildNumber)'

# Linking to Test Plan
- task: AssociateAutomation@0
  inputs:
    azureTestPlanId: 'YOUR_TEST_PLAN_ID'
    azureTestSuiteId: 'YOUR_SUITE_ID'

This creates traceability: a planned test case can show its automation history across builds.

Container Jobs

Run the entire job inside a Docker container.

jobs:
  - job: TestInContainer
    pool: { vmImage: 'ubuntu-latest' }
    container:
      image: 'eclipse-temurin:17-jdk'
      options: '--shm-size=2g'

    steps:
      - script: mvn test

The job's working directory mounts into the container. Useful when your pipeline needs a specific OS or toolchain not available on Microsoft-hosted agents.

Templates

For pipelines shared across projects use templates.

# templates/selenium-test.yml
parameters:
  - name: browser
    type: string
    default: 'chrome'
  - name: gridUrl
    type: string
  - name: tags
    type: string
    default: '@smoke'

jobs:
  - job: Test_${{ parameters.browser }}
    pool: { vmImage: 'ubuntu-latest' }
    steps:
      - script: |
          mvn test \
            -Dbrowser=${{ parameters.browser }} \
            -DgridUrl=${{ parameters.gridUrl }} \
            -Dgroups=${{ parameters.tags }}

In the main pipeline:

extends:
  template: templates/selenium-test.yml
  parameters:
    browser: 'chrome'
    gridUrl: 'http://grid:4444'
    tags: '@smoke,@critical'

Templates centralize common pipeline logic.

Scheduling

Run tests on a schedule.

schedules:
  - cron: '0 6 * * 1-5'
    displayName: 'Daily 6 AM weekday smoke'
    branches:
      include:
        - main
    always: true   # Run even if no code changes
  - cron: '0 22 * * *'
    displayName: 'Nightly regression'
    branches:
      include:
        - main

Cron in UTC. Different schedules can trigger different sets of stages via conditions.

Common Patterns

# Skip CI for docs-only changes
trigger:
  paths:
    exclude:
      - '*.md'
      - 'docs/**'

# Use specific commit message to skip
- script: |
    if [[ "$(Build.SourceVersionMessage)" == *"[skip ci]"* ]]; then
      echo "Skipping due to commit message"
      exit 0
    fi
    mvn test

# Conditional steps based on branch
- script: ./deploy-staging.sh
  condition: eq(variables['Build.SourceBranch'], 'refs/heads/develop')

# Retry on transient failures
- script: mvn test
  retryCountOnTaskFailure: 2

Conclusion

Azure DevOps Pipelines is the right CI for organizations embedded in the Microsoft ecosystem in 2026. YAML syntax, hosted and self-hosted agents, native test result publishing, and Azure Test Plans integration make it a complete platform for Selenium test suites. For organizations not on Azure, GitHub Actions (same parent company) is often a simpler choice for new projects.

If you are building an Azure DevOps pipeline for Selenium, start with a single-job pipeline, add matrix and parallel jobs as you scale, factor common logic into templates, and use Azure Test Plans for traceability. Browse the skills directory for Selenium AI agent skills and read Selenium Grid 4 for distributed test runtime.

Selenium Azure DevOps Pipeline Complete Guide 2026 | QASkills.sh