You keep a well-organized test repository: naming conventions, folders per module, a data section.
A framework is that organization for code — plus config so one switch changes environment or browser for every test at once.
"Framework" sounds grand; it's mostly agreed structure. Here's the layout your capstone will use — and a close cousin of what you'll meet in real jobs:
src
├─ main/java
│ ├─ pages/ LoginPage, ProductsPage, CartPage...
│ └─ utils/ ConfigReader, DriverFactory
├─ test/java
│ ├─ tests/ LoginTest, CheckoutTest, ApiTests...
│ └─ BaseTest.java
└─ test/resources
├─ config.properties base URL, browser, timeouts
└─ testdata/ CSVs for data-driven testsYour tests currently hardcode https://www.saucedemo.com. Real teams run the same tests against dev, staging, and prod. Externalize it (a ready sample file is in the downloads):
baseUrl=https://www.saucedemo.com browser=chrome timeoutSeconds=10
import java.util.Properties; import java.io.InputStream; public class ConfigReader { private static final Properties props = new Properties(); static { try (InputStream in = ConfigReader.class .getResourceAsStream("/config.properties")) { props.load(in); } catch (Exception e) { throw new RuntimeException("config.properties missing", e); } } public static String get(String key) { // -Dkey=value on the command line overrides the file — CI will use this return System.getProperty(key, props.getProperty(key)); } }
mvn test # uses config.properties mvn test "-DbaseUrl=https://staging.example.com" # override for one run
That System.getProperty fallback line is quietly the most professional line in this course: it's how CI pipelines point the same suite at any environment without touching code.
Browser creation moves out of BaseTest into one place that reads config — so browser=firefox or a -Dheadless=true flag changes every test at once. You'll finish this in the headless lesson. The principle in both pillars is the same one that gave you POM: every decision lives in exactly one place.
Fix: The file must live in src/test/resources (create the folder if needed) and the resource path starts with a slash: getResourceAsStream("/config.properties"). In IntelliJ, the folder should show the resources icon.
Fix: Two culprits: property keys are case-sensitive (-DbaseURL ≠ baseUrl), and PowerShell eats the flag unless quoted: mvn test "-DbaseUrl=https://staging.example.com".
open() to use ConfigReader.get("baseUrl").-DbaseUrl=https://www.saucedemo.com/ (trailing slash) to prove the override works.