Selenium Framework Integration with Azure DevOps: Complete Setup
How to integrate a Selenium test automation framework with Azure DevOps. Covers Maven/Gradle builds, TestNG/JUnit XML output, ChromeDriver configuration for CI, WebDriverManager setup, and publishing results to Azure Test Plans.
A Selenium framework integrated with Azure DevOps runs automatically on every commit, publishes results to Azure Test Plans, and flags regressions before they reach production. This guide covers a complete, production-ready integration.
Framework structure
selenium-framework/
├── pom.xml # Maven build file
├── azure-pipelines.yml # Pipeline definition
├── testng.xml # TestNG suite configuration
├── src/
│ ├── main/java/
│ │ └── framework/
│ │ ├── BaseTest.java # WebDriver lifecycle
│ │ ├── DriverFactory.java # Driver initialisation
│ │ └── pages/ # Page Object Model
│ │ ├── LoginPage.java
│ │ └── DashboardPage.java
│ └── test/java/
│ └── tests/
│ ├── AuthTests.java
│ └── CheckoutTests.java
└── src/test/resources/
├── config.properties # Environment config
└── test-data.json # Test data
Maven configuration (pom.xml)
<project>
<properties>
<selenium.version>4.21.0</selenium.version>
<testng.version>7.10.2</testng.version>
<wdm.version>5.8.0</wdm.version>
<maven.surefire.version>3.2.5</maven.surefire.version>
</properties>
<dependencies>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>${selenium.version}</version>
</dependency>
<dependency>
<groupId>io.github.bonigarcia</groupId>
<artifactId>webdrivermanager</artifactId>
<version>${wdm.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>${testng.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${maven.surefire.version}</version>
<configuration>
<suiteXmlFiles>
<suiteXmlFile>testng.xml</suiteXmlFile>
</suiteXmlFiles>
<rerunFailingTestsCount>1</rerunFailingTestsCount>
</configuration>
</plugin>
</plugins>
</build>
</project>BaseTest with CI-aware configuration
public class BaseTest {
protected WebDriver driver;
protected String baseUrl;
@BeforeMethod
@Parameters({"browser"})
public void setUp(@Optional("chrome") String browser) {
baseUrl = System.getenv("BASE_URL") != null
? System.getenv("BASE_URL")
: "http://localhost:3000";
boolean isCI = System.getenv("TF_BUILD") != null; // Azure Pipelines sets TF_BUILD
if (browser.equals("chrome")) {
WebDriverManager.chromedriver().setup();
ChromeOptions options = new ChromeOptions();
if (isCI) {
options.addArguments("--headless=new", "--no-sandbox",
"--disable-dev-shm-usage", "--window-size=1920,1080");
}
driver = new ChromeDriver(options);
} else if (browser.equals("firefox")) {
WebDriverManager.firefoxdriver().setup();
FirefoxOptions ffOptions = new FirefoxOptions();
if (isCI) ffOptions.addArguments("--headless");
driver = new FirefoxDriver(ffOptions);
}
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10));
}
@AfterMethod(alwaysRun = true)
public void tearDown(ITestResult result) {
if (result.getStatus() == ITestResult.FAILURE && driver != null) {
captureScreenshot(result.getName());
}
if (driver != null) driver.quit();
}
private void captureScreenshot(String testName) {
TakesScreenshot ts = (TakesScreenshot) driver;
File src = ts.getScreenshotAs(OutputType.FILE);
try {
FileUtils.copyFile(src, new File("test-screenshots/" + testName + ".png"));
} catch (IOException e) {
System.err.println("Screenshot failed: " + e.getMessage());
}
}
}TestNG suite configuration
<!-- testng.xml -->
<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
<suite name="Regression Suite" parallel="classes" thread-count="3">
<listeners>
<listener class-name="org.testng.reporters.JUnitXMLReporter"/>
</listeners>
<test name="Auth Tests">
<parameter name="browser" value="chrome"/>
<classes>
<class name="tests.AuthTests"/>
</classes>
</test>
<test name="Checkout Tests">
<parameter name="browser" value="chrome"/>
<classes>
<class name="tests.CheckoutTests"/>
</classes>
</test>
</suite>Azure Pipelines YAML
trigger:
branches:
include: [main, release/*]
pool:
vmImage: ubuntu-latest
variables:
- group: selenium-env-vars
- name: MAVEN_CACHE_FOLDER
value: $(Pipeline.Workspace)/.m2/repository
- name: MAVEN_OPTS
value: -Dmaven.repo.local=$(MAVEN_CACHE_FOLDER)
stages:
- stage: SeleniumRegression
displayName: Selenium Regression Suite
jobs:
- job: TestNG
displayName: TestNG Selenium Tests
timeoutInMinutes: 45
steps:
- task: Cache@2
displayName: Cache Maven packages
inputs:
key: 'maven | "$(Agent.OS)" | **/pom.xml'
restoreKeys: 'maven | "$(Agent.OS)"'
path: $(MAVEN_CACHE_FOLDER)
- task: JavaToolInstaller@0
displayName: Set up Java 17
inputs:
versionSpec: '17'
jdkArchitectureOption: x64
jdkSourceOption: PreInstalled
- script: mkdir -p test-screenshots test-results
displayName: Create output directories
- script: |
mvn test \
-Dsurefire.rerunFailingTestsCount=1 \
-Dmaven.test.failure.ignore=true \
$(MAVEN_OPTS)
displayName: Run Selenium tests
env:
BASE_URL: $(STAGING_URL)
TF_BUILD: $(TF_BUILD)
- 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
inputs:
targetPath: test-screenshots
artifact: selenium-screenshots
condition: always()Common errors and fixes
Error: java.lang.UnsatisfiedLinkError for ChromeDriver on Linux
Fix: Add --no-sandbox and --disable-dev-shm-usage Chrome options. On Azure Linux agents, the sandbox requires privileges that CI environments don't have.
Error: Maven downloads all dependencies on every pipeline run (slow)
Fix: Add the Cache@2 task to cache the .m2 repository. The cache key based on pom.xml means it only invalidates when dependencies change.
Error: TestNG reports show 0 tests even though tests ran
Fix: The JUnit XML reporter in TestNG generates to surefire-reports by default. Verify the path with find . -name "TEST-*.xml" in a script step.
Error: WebDriverManager failed to download ChromeDriver
Fix: Azure agents have Chrome pre-installed. Use WebDriverManager.chromedriver().browserVersion("installed").setup() to use the agent's installed Chrome version without downloading.
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