Answer
Multi-Browser, Multi-Environment Selenium Framework
Architecture Overview
CODE
testng.xml / Maven CLI
│
┌──────┴──────┐
│ BaseTest │
│ @BeforeMethod│
└──────┬──────┘
│
┌───────────┴────────────┐
│ DriverFactory │
│ browser=chrome/ff/edge │
│ mode=local/grid/sauce │
└───────────┬────────────┘
│ │ │
ChromeDriver FirefoxDriver RemoteWebDriver (Grid)
│
┌───────────┴────────────┐
│ ConfigReader │
│ env=staging/qa/prod │
└────────────────────────┘
Environment Config Files
PROPERTIES
# config/staging.properties
base.url=https://staging.automateqa.online
db.url=jdbc:postgresql://staging-db:5432/automateqa
api.base=https://api.staging.automateqa.online
test.user=staging_test@automateqa.com
test.pass=StagingPass@123
# config/qa.properties
base.url=https://qa.automateqa.online
api.base=https://api.qa.automateqa.online
# config/prod.properties (read-only smoke tests only!)
base.url=https://automateqa.online
test.user=prod_smoke@automateqa.com
DriverFactory — Supports Local + Grid
Java
public class DriverFactory {
public static WebDriver createDriver(String browser, boolean headless)
throws MalformedURLException {
String gridUrl = ConfigReader.get("grid.url"); // "" or actual Grid URL
if (gridUrl != null && !gridUrl.isEmpty()) {
return createRemoteDriver(browser, headless, gridUrl);
} else {
return createLocalDriver(browser, headless);
}
}
private static WebDriver createLocalDriver(String browser, boolean headless) {
switch (browser.toLowerCase()) {
case "firefox":
FirefoxOptions ffOpts = new FirefoxOptions();
if (headless) ffOpts.addArguments("--headless");
return new FirefoxDriver(ffOpts);
case "edge":
EdgeOptions edgeOpts = new EdgeOptions();
if (headless) edgeOpts.addArguments("--headless");
return new EdgeDriver(edgeOpts);
default: // chrome
ChromeOptions opts = new ChromeOptions();
if (headless) opts.addArguments("--headless=new",
"--no-sandbox", "--disable-dev-shm-usage");
return new ChromeDriver(opts);
}
}
private static WebDriver createRemoteDriver(String browser, boolean headless, String gridUrl)
throws MalformedURLException {
MutableCapabilities options;
switch (browser.toLowerCase()) {
case "firefox": options = new FirefoxOptions(); break;
case "edge": options = new EdgeOptions(); break;
default: options = new ChromeOptions(); break;
}
if (headless) ((ChromeOptions) options).addArguments("--headless=new");
return new RemoteWebDriver(new URL(gridUrl), options);
}
}
BaseTest — Reads From Config
Java
public class BaseTest {
@Parameters({"browser", "env"})
@BeforeMethod(alwaysRun = true)
public void setUp(@Optional("chrome") String browser,
@Optional("staging") String env)
throws MalformedURLException {
// Set env so ConfigReader loads the right .properties file
System.setProperty("env", env);
System.setProperty("browser", browser);
boolean headless = ConfigReader.isHeadless();
WebDriver driver = DriverFactory.createDriver(browser, headless);
driver.manage().window().maximize();
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10));
DriverManager.setDriver(driver);
String baseUrl = ConfigReader.getBaseUrl();
driver.get(baseUrl);
}
@AfterMethod(alwaysRun = true)
public void tearDown(ITestResult result) {
if (result.getStatus() == ITestResult.FAILURE) {
ScreenshotUtil.capture(result.getName());
}
DriverManager.quitDriver();
}
}
testng.xml — Cross-Browser on Staging
XML
<suite name="Cross-Browser Staging" parallel="tests" thread-count="3">
<parameter name="env" value="staging"/>
<test name="Chrome-Staging">
<parameter name="browser" value="chrome"/>
<classes><class name="com.automateqa.tests.SmokeTest"/></classes>
</test>
<test name="Firefox-Staging">
<parameter name="browser" value="firefox"/>
<classes><class name="com.automateqa.tests.SmokeTest"/></classes>
</test>
<test name="Edge-Staging">
<parameter name="browser" value="edge"/>
<classes><class name="com.automateqa.tests.SmokeTest"/></classes>
</test>
</suite>
GitHub Actions — Multi-Environment Matrix
YAML
name: Multi-Environment Tests
on:
push:
branches: [main]
schedule:
- cron: '0 */6 * * *' # Every 6 hours
jobs:
test:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
env: [staging, qa]
browser: [chrome, firefox]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
java-version: '21'
- name: Start Selenium Grid
run: |
docker network create grid
docker run -d -p 4444:4444 --network grid selenium/standalone-chrome:latest
sleep 10
- name: Run Tests — ${{ matrix.env }} / ${{ matrix.browser }}
run: |
mvn test \
-Denv=${{ matrix.env }} \
-Dbrowser=${{ matrix.browser }} \
-Dheadless=true \
-Dgrid.url=http://localhost:4444 \
-Dbase.url=${{ secrets[format('{0}_BASE_URL', matrix.env)] }}
- name: Upload Report
uses: actions/upload-artifact@v4
if: always()
with:
name: report-${{ matrix.env }}-${{ matrix.browser }}
path: test-output/
