🧱 Framework & CI
A real framework you can explain
“Explain your framework” is one of the most-asked automation questions. Download a working Selenium + TestNG framework that tests this site — page objects, parallel runs, config, data-driven tests, screenshots on failure — plus ready GitHub Actions and Jenkins pipelines.
The layers
1
Tests
Only the scenario and its assertions: LoginTest, ShopTest. Groups (smoke, regression) and data providers live here.
2
Page objects
One class per page. Locators stay private; methods are user actions that return the next page.
3
Core
BaseTest (browser per test), BasePage (waits, scroll-to-centre), DriverFactory (ThreadLocal driver), Config, the screenshot listener and a retry analyzer.
4
Build & config
pom.xml pins the versions, testng.xml runs 4 tests in parallel, config.properties holds defaults you override per run.
5
CI
A GitHub Actions workflow and a Jenkinsfile that run the same Maven command and keep the reports.
Browse the code
Every file below is in the download. The 11 tests pass against this site — including 4 at a time in parallel — and the failure screenshot, retry and group filtering were each checked on purpose.
package com.practiceqaautomation.framework.tests;
import static org.testng.Assert.*;
import com.practiceqaautomation.framework.core.BaseTest;
import com.practiceqaautomation.framework.pages.LoginFormPage;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
public class LoginTest extends BaseTest {
@Test(groups = {"smoke", "regression"})
public void validLoginWelcomesTheUser() {
String message = new LoginFormPage(driver()).open().logIn("student", "Password123").successMessage();
assertTrue(message.contains("welcome, student!"), message);
}
@DataProvider(name = "invalidLogins")
public Object[][] invalidLogins() {
return new Object[][] {
{"student", "wrong", "Invalid username or password."},
{"", "Password123", "Both username and password are required."},
{"student", "", "Both username and password are required."},
};
}
@Test(groups = "regression", dataProvider = "invalidLogins")
public void invalidLoginShowsAnError(String username, String password, String expected) {
assertEquals(new LoginFormPage(driver()).open().logIn(username, password).errorMessage(), expected);
}
@Test(groups = "regression")
public void accountLocksAfterThreeWrongPasswords() {
LoginFormPage page = new LoginFormPage(driver()).open();
for (int i = 1; i <= 3; i++) page.logIn("student", "wrong-" + i);
assertEquals(page.errorMessage(), "Too many failed attempts. Account locked — refresh to try again.");
assertFalse(page.isSubmitEnabled());
}
}Run it
mvn test # everything in testng.xml, 4 tests at a time
mvn test -Dgroups=smoke # just the smoke group
HEADLESS=true mvn test # no browser window (what CI does)
mvn test -Dtest=LoginTest # one class
mvn test -DbaseUrl=http://localhost:3000 # another environmentReports: target/surefire-reports · failure screenshots: target/screenshots.
Run it in CI
- GitHub Actions — push the project to a GitHub repository;
.github/workflows/tests.ymlruns the smoke group on every push and pull request, and the full regression from the Actions tab (“Run workflow”). Reports are kept as a build artifact. - Jenkins — create a Pipeline job pointing at the repository; the
Jenkinsfileasks which group and environment to run, publishes the test results and archives screenshots. - How we checked them: the workflow passes actionlint and the Jenkinsfile parses as valid Groovy, and the exact Maven commands both run were executed locally. We didn't run them on GitHub's or a Jenkins server — please report anything that needs a tweak.
Use it in interviews
“Explain your framework”
Walk the layers above, top to bottom, and say why each exists.
“How do you run tests in parallel?”
testng.xml parallel="methods" + a ThreadLocal driver, so no test shares a browser.
“How do you switch environments?”
-DbaseUrl=… or an environment variable overrides config.properties — no code change.
“What happens when a test fails?”
A listener saves a screenshot; CI keeps reports and screenshots as build artifacts.
“How do you handle flaky tests?”
Fix the cause first; one retry only for a slow end-to-end test, and track retries.
“How does it run in CI?”
Smoke on every push, full regression on demand — GitHub Actions or Jenkins, same command.
More: Framework & CI interview questions · add BDD with Cucumber · Selenium guides for every app