Selenium Framework + Azure DevOps Integration
How to integrate a Selenium test automation framework with Azure DevOps. Covers Maven/Gradle builds, TestNG/JUnit XML output, ChromeDriver configuration.
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)
XML1<project> 2 <properties> 3 <selenium.version>4.21.0</selenium.version> 4 <testng.version>7.10.2</testng.version> 5 <wdm.version>5.8.0</wdm.version> 6 <maven.surefire.version>3.2.5</maven.surefire.version> 7 </properties> 8 9 <dependencies> 10 <dependency> 11 <groupId>org.seleniumhq.selenium</groupId> 12 <artifactId>selenium-java</artifactId> 13 <version>${selenium.version}</version> 14 </dependency> 15 <dependency> 16 <groupId>io.github.bonigarcia</groupId> 17 <artifactId>webdrivermanager</artifactId> 18 <version>${wdm.version}</version> 19 <scope>test</scope> 20 </dependency> 21 <dependency> 22 <groupId>org.testng</groupId> 23 <artifactId>testng</artifactId> 24 <version>${testng.version}</version> 25 <scope>test</scope> 26 </dependency> 27 </dependencies> 28 29 <build> 30 <plugins> 31 <plugin> 32 <groupId>org.apache.maven.plugins</groupId> 33 <artifactId>maven-surefire-plugin</artifactId> 34 <version>${maven.surefire.version}</version> 35 <configuration> 36 <suiteXmlFiles> 37 <suiteXmlFile>testng.xml</suiteXmlFile> 38 </suiteXmlFiles> 39 <rerunFailingTestsCount>1</rerunFailingTestsCount> 40 </configuration> 41 </plugin> 42 </plugins> 43 </build> 44</project>
BaseTest with CI-aware configuration
JAVA1public class BaseTest { 2 protected WebDriver driver; 3 protected String baseUrl; 4 5 @BeforeMethod 6 @Parameters({"browser"}) 7 public void setUp(@Optional("chrome") String browser) { 8 baseUrl = System.getenv("BASE_URL") != null 9 ? System.getenv("BASE_URL") 10 : "http://localhost:3000"; 11 12 boolean isCI = System.getenv("TF_BUILD") != null; // Azure Pipelines sets TF_BUILD 13 14 if (browser.equals("chrome")) { 15 WebDriverManager.chromedriver().setup(); 16 ChromeOptions options = new ChromeOptions(); 17 if (isCI) { 18 options.addArguments("--headless=new", "--no-sandbox", 19 "--disable-dev-shm-usage", "--window-size=1920,1080"); 20 } 21 driver = new ChromeDriver(options); 22 } else if (browser.equals("firefox")) { 23 WebDriverManager.firefoxdriver().setup(); 24 FirefoxOptions ffOptions = new FirefoxOptions(); 25 if (isCI) ffOptions.addArguments("--headless"); 26 driver = new FirefoxDriver(ffOptions); 27 } 28 29 driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10)); 30 } 31 32 @AfterMethod(alwaysRun = true) 33 public void tearDown(ITestResult result) { 34 if (result.getStatus() == ITestResult.FAILURE && driver != null) { 35 captureScreenshot(result.getName()); 36 } 37 if (driver != null) driver.quit(); 38 } 39 40 private void captureScreenshot(String testName) { 41 TakesScreenshot ts = (TakesScreenshot) driver; 42 File src = ts.getScreenshotAs(OutputType.FILE); 43 try { 44 FileUtils.copyFile(src, new File("test-screenshots/" + testName + ".png")); 45 } catch (IOException e) { 46 System.err.println("Screenshot failed: " + e.getMessage()); 47 } 48 } 49}
TestNG suite configuration
XML1<!-- testng.xml --> 2<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd"> 3<suite name="Regression Suite" parallel="classes" thread-count="3"> 4 <listeners> 5 <listener class-name="org.testng.reporters.JUnitXMLReporter"/> 6 </listeners> 7 <test name="Auth Tests"> 8 <parameter name="browser" value="chrome"/> 9 <classes> 10 <class name="tests.AuthTests"/> 11 </classes> 12 </test> 13 <test name="Checkout Tests"> 14 <parameter name="browser" value="chrome"/> 15 <classes> 16 <class name="tests.CheckoutTests"/> 17 </classes> 18 </test> 19</suite>
Azure Pipelines YAML
YAML1trigger: 2 branches: 3 include: [main, release/*] 4 5pool: 6 vmImage: ubuntu-latest 7 8variables: 9 - group: selenium-env-vars 10 - name: MAVEN_CACHE_FOLDER 11 value: $(Pipeline.Workspace)/.m2/repository 12 - name: MAVEN_OPTS 13 value: -Dmaven.repo.local=$(MAVEN_CACHE_FOLDER) 14 15stages: 16 - stage: SeleniumRegression 17 displayName: Selenium Regression Suite 18 jobs: 19 - job: TestNG 20 displayName: TestNG Selenium Tests 21 timeoutInMinutes: 45 22 steps: 23 - task: Cache@2 24 displayName: Cache Maven packages 25 inputs: 26 key: 'maven | "$(Agent.OS)" | **/pom.xml' 27 restoreKeys: 'maven | "$(Agent.OS)"' 28 path: $(MAVEN_CACHE_FOLDER) 29 30 - task: JavaToolInstaller@0 31 displayName: Set up Java 17 32 inputs: 33 versionSpec: '17' 34 jdkArchitectureOption: x64 35 jdkSourceOption: PreInstalled 36 37 - script: mkdir -p test-screenshots test-results 38 displayName: Create output directories 39 40 - script: | 41 mvn test \ 42 -Dsurefire.rerunFailingTestsCount=1 \ 43 -Dmaven.test.failure.ignore=true \ 44 $(MAVEN_OPTS) 45 displayName: Run Selenium tests 46 env: 47 BASE_URL: $(STAGING_URL) 48 TF_BUILD: $(TF_BUILD) 49 50 - task: PublishTestResults@2 51 displayName: Publish TestNG results 52 inputs: 53 testResultsFormat: JUnit 54 testResultsFiles: '**/surefire-reports/TEST-*.xml' 55 testRunTitle: Selenium TestNG — $(Build.BuildNumber) 56 mergeTestResults: true 57 condition: always() 58 59 - task: PublishPipelineArtifact@1 60 displayName: Upload screenshots 61 inputs: 62 targetPath: test-screenshots 63 artifact: selenium-screenshots 64 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.
Share this article
Follow for more
Follow me on social media for more developer tips, tricks, and tutorials. Let's connect and build something great together!