PracticeQAAutomation logoPracticeQAAutomation

🧱 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.

⬇ Download the projectJava 17+ · Maven · Chrome · MIT licence

The layers

  1. 1

    Tests

    Only the scenario and its assertions: LoginTest, ShopTest. Groups (smoke, regression) and data providers live here.

  2. 2

    Page objects

    One class per page. Locators stay private; methods are user actions that return the next page.

  3. 3

    Core

    BaseTest (browser per test), BasePage (waits, scroll-to-centre), DriverFactory (ThreadLocal driver), Config, the screenshot listener and a retry analyzer.

  4. 4

    Build & config

    pom.xml pins the versions, testng.xml runs 4 tests in parallel, config.properties holds defaults you override per run.

  5. 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.

src/test/java/com/practiceqaautomation/framework/tests/LoginTest.java
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

terminal
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 environment

Reports: target/surefire-reports · failure screenshots: target/screenshots.

Run it in CI

  • GitHub Actions — push the project to a GitHub repository; .github/workflows/tests.yml runs 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 Jenkinsfile asks 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