A Selenium WebDriver + Java + TestNG automation framework, built from scratch with every architectural decision and problem documented publicly.
This is not a course project. It's a working framework where every architectural decision, every problem, and every solution is documented publicly. Built by a QA engineer with 4+ years of experience testing enterprise ERP systems.
| Layer | Technology |
|---|---|
| Language | Java |
| Automation | Selenium WebDriver |
| Test Framework | TestNG |
| Reporting | Allure Reports |
| Data Source | Excel (Apache POI) |
| Build | Maven |
| CI/CD | GitHub Actions |
| Report Hosting | GitHub Pages |
Every push to main triggers an automated pipeline:
Push to main
β
GitHub Actions (Ubuntu + Java 17 + Chrome headless)
β
mvn clean test (96 tests)
β
Allure Report generated
β
Published to GitHub Pages
Live report: cesarbeassuarez.github.io/selenium-java-framework
Note
The report includes intentional failures. I modified expected values to force mismatches against the web app, so the Allure report shows real failure screenshots, logs, and error traces. This demonstrates how the framework handles failures β not actual bugs in the code.
The pipeline configures headless Chrome with forced Spanish locale and desktop resolution (1920Γ1080 via CDP), so tests behave identically to local execution. Timeout is increased from 10s to 30s for CI environments.
Why this structure:
main/vstest/separation β the framework (Pages, utils, config) lives inmain. Tests live intest. This lets me reuse Pages without coupling them to a specific test, and I could publish the framework as a library if needed.- Grouped by feature, not by test type β tests are organized by feature (
login/,clientes/), not by category (smoke/,regression/). When a clientes flow breaks, I go to one place. Test type is handled with TestNG groups, not folders. - Externalized config β URL, credentials, browser, and timeouts live in
config.properties, not hardcoded. Switching environments means editing one file. - One Page Object per screen β each class in
pages/represents a real screen. If a selector changes, I touch one class. Tests never see aBy.id(...).
selenium-java/
βββ .github/
β βββ workflows/
β βββ tests.yml # CI: runs the suite on every push/PR via GitHub Actions
βββ .allure/ # Local Allure config
βββ .idea/ # IntelliJ config (gitignored)
βββ allure-results/ # Raw output from each run. Allure reads this to build the report
βββ src/
β βββ main/java/com/cesar/qa/
β β βββ base/
β β β βββ BasePage.java # Parent class for all Pages. Centralizes waits, common actions, logging
β β βββ config/
β β β βββ ConfigReader.java # Reads config.properties (URL, credentials, browser, timeouts)
β β β βββ DriverManager.java # Creates/manages the WebDriver. ThreadLocal for parallel execution
β β βββ pages/
β β β βββ LoginPage.java # POM: selectors and actions for the login screen
β β β βββ DashboardPage.java # POM: selectors and actions for the post-login dashboard
β β β βββ ClientesPage.java # POM: clientes CRUD (create, search, edit, grid)
β β βββ utils/
β β βββ AllureScreenshot.java # Attaches screenshots to the Allure report (on failure or key steps)
β β βββ check/ # Reusable validation/assertion helpers
β β βββ ExcelReader.java # Reads .xlsx for data-driven testing via Apache POI
β βββ test/
β βββ java/com/cesar/qa/
β β βββ base/
β β β βββ BaseTest.java # Test lifecycle: @BeforeMethod, @AfterMethod, driver init
β β βββ data/
β β β βββ TestData.java # Generic TestNG DataProviders
β β β βββ ClientesTestData.java # DataProviders specific to the Clientes module
β β βββ listeners/
β β β βββ AllureListener.java # TestNG hook: captures screenshots on failure, logs steps
β β βββ tests/
β β βββ login/
β β β βββ LoginPositiveTests.java # Happy-path login cases
β β β βββ LoginNegativeTests.java # Invalid credentials, empty fields, lockouts
β β βββ clientes/
β β βββ ClientesTests.java # Clientes module flows
β βββ resources/
β βββ testdata/
β β βββ clientes-data.xlsx # External data for parameterized tests
β βββ allure.properties # Report config (results dir, etc.)
β βββ config.properties # URL, credentials, browser, timeouts
β βββ logback.xml # Logging config (levels, format, appenders)
β βββ testng.xml # Suite: which tests to run, order, parallel or not
βββ pom.xml # Maven dependencies (Selenium, TestNG, Allure, WebDriverManager, POI)
src/main/java β framework code (what does the testing).
src/test/java β the tests themselves (what gets executed).
Standard Maven split: the framework lives in main, tests in test. This way I can reuse Pages and utils without tying them to a specific test.
Parent classes. Avoid code duplication:
BasePagecentralizes methods common to any Page (safe click, waits, type with prior clear).BaseTestcentralizes the test lifecycle (open driver before, close after).
Everything that changes between environments (local, staging, CI) lives here.
ConfigReaderreadsconfig.properties. If the URL changes tomorrow, I edit one file, not 20 tests.DriverManagermanages theWebDriverinstance. UsesThreadLocalwith parallel TestNG execution in mind.
Pure Page Object Model. One class per screen:
- Private selectors inside (not exposed to tests).
- Public methods that represent user actions (
loginAs(user, pass),searchClient(name)). - Tests never see a
By.id(...). If a selector changes, I touch one class.
Cross-cutting tools:
AllureScreenshotβ screenshots attached to the report.ExcelReaderβ.xlsxreading with Apache POI for data-driven tests.check/β reusable validation helpers, to avoid repeating verbose asserts.
TestNG DataProviders. I separate generic ones (TestData) from module-specific ones (ClientesTestData). Each test pulls only what it needs.
AllureListener hooks into TestNG events (failure, success, skip) and connects them to the report: screenshot on failure, step logging, context attachments.
Tests organized by feature module (login/, clientes/), not by test type. Easier to maintain when grouped by business feature.
Config and external data:
config.propertiesβ which environment, which browser, which timeouts.testng.xmlβ the suite. Defines what runs, in what order, with which listeners, parallel or not.logback.xmlβ how the framework logs.testdata/β data files (.xlsx) for parameterized tests.
GitHub Actions pipeline. Every push runs the suite and publishes the Allure report to GitHub Pages. CI without a self-hosted server, free.
Raw output from each run. Not committed (gitignored). Allure consumes it to generate the HTML report.
Maven dependencies and plugins. Project's backbone: Selenium, TestNG, WebDriverManager, Allure, and Apache POI versions live here.
Each entry represents a real development iteration. Full context on decisions and tradeoffs documented on my blog.
| # | Focus | Date |
|---|---|---|
| 1 | Selenium + Java desde cero β Why Selenium over Cypress, Java over Python, TestNG over JUnit. First test executed. | 30 Dec 2025 |
| 2 | Por quΓ© borrΓ© todo y volvΓ al dΓa 1 β Had a full framework built with AI. Didn't understand it. Deleted everything and started from scratch. | 08 Jan 2026 |
| 3 | pom.xml, Logback, estructura de carpetas β Maven config, logging with Logback, base/pages/utils/test folder structure. | 09 Jan 2026 |
| 4 | DejΓ© de hardcodear: config.properties y DriverManager β Eliminated hardcoded values. Centralized driver and config management. | 13 Jan 2026 |
| 5 | Localizadores en Selenium β DOM, selectors, locator hierarchy from id to XPath. Real login test. | 16 Jan 2026 |
| 6 | Esperas ImplΓcitas, ExplΓcitas y Fluent Waits β Why Thread.sleep kills tests. implicitWait vs WebDriverWait vs FluentWait. | 21 Jan 2026 |
| 7 | Page Object Model: separar Pages de Tests β LoginPage, DashboardPage anatomy. Pages interact with UI, Tests decide pass/fail. | 30 Jan 2026 |
| 8 | Framework TestNG β Migrated from manual main() to @Test, @BeforeMethod, BaseTest, testng.xml. | 18 Feb 2026 |
| 9 | DataProviders y assertions reales β Replaced check.java with TestNG Assert. DataProviders in separate class. 5 tests, clean separation. | 20 Feb 2026 |
| 10 | Validar grilla de clientes contra Excel β SlickGrid, virtual scrolling, Apache POI. 91 records validated in 1 min. | 25 Feb 2026 |
| 11 | Allure Reports β reporting profesional β @Step, @Description, @Severity, auto screenshots on failure. Full implementation. | 26 Feb 2026 |
| 12 | CI/CD con GitHub Actions β Headless Chrome, Allure report generation, GitHub Pages deployment. +15 commits fixing real CI problems. | 02 Mar 2026 |
- Not a tutorial project. Every decision reflects real testing experience on enterprise systems.
- Documented tradeoffs. I explain why, not just how.
- Built in public. Progress, mistakes, and iterations β all visible.
- CI/CD integrated. Tests run automatically, reports are public.
This lab is intentionally documented in public and still evolving. Current known gaps:
These are intentional next steps to strengthen data integrity validations.
- #1 Grid validation: detect extra/missing rows (web vs Excel)
- #2 Grid validation: validate row order (or define order-insensitive strategy)
- Blog: cesarbeassuarez.dev
- Live report: cesarbeassuarez.github.io/selenium-java-framework
- LinkedIn: linkedin.com/in/cesarbeassuarez