Skip to main content
Back to blog

Azure DevOps Integration with Cloud Testing Platforms

How to integrate Azure DevOps with cloud testing platforms — BrowserStack, Sauce Labs, and LambdaTest. Covers service connection setup, pipeline YAML for cloud browser tests, result reporting, and cost optimisation strategies.

InnovateBits3 min read
Share

Cloud testing platforms give your Azure DevOps pipeline access to thousands of real browsers and devices without managing any infrastructure. This guide covers integration with BrowserStack, Sauce Labs, and LambdaTest.


Why cloud testing platforms with Azure DevOps

Microsoft-hosted agents provide Chrome and Firefox. For comprehensive cross-browser and mobile testing you need real Safari on iOS, real Android devices, older browser versions, and specific OS combinations. Cloud platforms provide all of this on demand.


BrowserStack integration

Service connection setup

  1. Go to Project Settings → Service connections → New
  2. Type: BrowserStack (from Marketplace extension)
  3. Enter: BrowserStack username and access key

Or use a generic service connection and reference credentials via variable group.

Pipeline YAML — Playwright on BrowserStack

trigger:
  branches:
    include: [main]
 
pool:
  vmImage: ubuntu-latest
 
variables:
  - group: browserstack-credentials
 
steps:
  - task: NodeTool@0
    inputs:
      versionSpec: '20.x'
 
  - script: npm ci
    displayName: Install packages
 
  - script: npm install -g browserstack-local
    displayName: Install BrowserStack Local
 
  - script: |
      # Start BrowserStack Local tunnel for staging (non-public) environments
      BrowserStackLocal --key $(BROWSERSTACK_KEY) \
        --local-identifier $(Build.BuildId) &
      sleep 5
    displayName: Start local tunnel
    condition: eq(variables['STAGING_IS_LOCAL'], 'true')
 
  - script: |
      npx playwright test \
        --config=playwright.browserstack.config.ts
    displayName: Run tests on BrowserStack
    env:
      BROWSERSTACK_USERNAME: $(BROWSERSTACK_USERNAME)
      BROWSERSTACK_ACCESS_KEY: $(BROWSERSTACK_KEY)
      BUILD_NAME: $(Build.BuildNumber)
      BUILD_ID: $(Build.BuildId)
 
  - task: PublishTestResults@2
    inputs:
      testResultsFormat: JUnit
      testResultsFiles: test-results/browserstack.xml
      testRunTitle: BrowserStack — $(Build.BuildNumber)
    condition: always()

Playwright BrowserStack config

// playwright.browserstack.config.ts
import { defineConfig } from '@playwright/test'
 
export default defineConfig({
  reporter: [
    ['junit', { outputFile: 'test-results/browserstack.xml' }],
    ['html'],
  ],
  projects: [
    {
      name: 'Chrome/Windows10',
      use: {
        connectOptions: {
          wsEndpoint: `wss://cdp.browserstack.com/playwright?caps=${encodeURIComponent(JSON.stringify({
            browser: 'chrome',
            browser_version: 'latest',
            os: 'Windows',
            os_version: '10',
            name: 'Chrome Windows 10',
            build: process.env.BUILD_NAME,
            'browserstack.username': process.env.BROWSERSTACK_USERNAME,
            'browserstack.accessKey': process.env.BROWSERSTACK_ACCESS_KEY,
          }))}`,
        },
      },
    },
    {
      name: 'Safari/macOS13',
      use: {
        connectOptions: {
          wsEndpoint: `wss://cdp.browserstack.com/playwright?caps=${encodeURIComponent(JSON.stringify({
            browser: 'playwright-webkit',
            os: 'OS X',
            os_version: 'Ventura',
            name: 'Safari macOS Ventura',
            build: process.env.BUILD_NAME,
            'browserstack.username': process.env.BROWSERSTACK_USERNAME,
            'browserstack.accessKey': process.env.BROWSERSTACK_ACCESS_KEY,
          }))}`,
        },
      },
    },
  ],
})

Sauce Labs integration

steps:
  - script: npm ci
 
  - script: |
      npx playwright test \
        --config=playwright.saucelabs.config.ts
    env:
      SAUCE_USERNAME: $(SAUCE_USERNAME)
      SAUCE_ACCESS_KEY: $(SAUCE_ACCESS_KEY)
// playwright.saucelabs.config.ts
export default defineConfig({
  projects: [{
    name: 'Sauce Chrome',
    use: {
      connectOptions: {
        wsEndpoint: `wss://ondemand.us-west-1.saucelabs.com/wd/hub?username=${process.env.SAUCE_USERNAME}&access_key=${process.env.SAUCE_ACCESS_KEY}`,
      },
    },
  }],
})

LambdaTest integration

steps:
  - script: |
      npx playwright test \
        --config=playwright.lambdatest.config.ts
    env:
      LT_USERNAME: $(LT_USERNAME)
      LT_ACCESS_KEY: $(LT_ACCESS_KEY)

Cost optimisation strategies

Cloud platforms charge per test minute. Keep costs manageable:

# Run cloud tests only on main branch merges, not every PR
- stage: CloudBrowserTests
  condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main'))
  jobs:
    - job: BrowserStack
      steps:
        # Run only smoke tests on cloud — full regression locally
        - script: npx playwright test --grep @cross-browser
# Limit parallel sessions to control costs
# In playwright.browserstack.config.ts:
workers: 3,  # BrowserStack free tier: 5 parallel sessions — use 3 for safety

Common errors and fixes

Error: BrowserStack tests fail with "Authentication failed" Fix: Verify BROWSERSTACK_USERNAME and BROWSERSTACK_ACCESS_KEY are set correctly. The username is your email address, not a generated string.

Error: Tests connect but can't reach the staging URL Fix: If staging is not publicly accessible, start the BrowserStack Local tunnel before running tests. The tunnel creates a secure connection between BrowserStack's nodes and your private environment.

Error: Test results don't appear on BrowserStack dashboard Fix: Include build and name in the capabilities object. Without these, BrowserStack creates sessions with auto-generated names that don't correlate to your pipeline runs.

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