Automation Framework Best Practices
This is the last lesson in the track, so instead of a new API, here's a checklist that ties everything together — the habits that separate a RecipeBox suite that's a genuine asset to the team from one that everyone quietly starts ignoring after a few months of flaky failures.
1. Use Explicit Waits — Never Thread.sleep()
Thread.sleep(5000) either wastes 5 seconds when the page was ready in 1, or fails anyway when the page took 6. An explicit wait polls for the actual condition and moves on the instant it's true:
// Avoid this
Thread.sleep(5000);
driver.findElement(By.id("saveRecipeBtn")).click();
// Prefer this
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
wait.until(ExpectedConditions.elementToBeClickable(By.id("saveRecipeBtn"))).click();
2. Keep Locators in Page Objects, Never Inline in Tests
If By.id("saveRecipeBtn") shows up directly inside a @Test method, the day that ID changes you're hunting through every test file. Keep every locator inside a Page Object (see the Page Object Model and Page Factory lessons) — the test class should only call readable methods like addRecipePage.save().
3. Make Tests Independent
No test should depend on another test's leftover state — order should never matter. If testSearchRecipe only passes because testAddRecipe happened to run first and left a recipe in the list, you have a hidden dependency that breaks the moment someone runs tests individually, reorders the suite, or runs it in parallel (see the Grid & Parallel Execution lesson). Each test should set up its own data and clean up after itself.
4. Use Meaningful Assertion Messages
// Unhelpful when it fails at 2 AM in a CI log
Assert.assertTrue(addRecipePage.isRecipeSaved(title));
// Tells you exactly what broke, without opening a debugger
Assert.assertTrue(addRecipePage.isRecipeSaved(title),
"Recipe '" + title + "' did not appear in the list after saving");
5. Run in CI on Every Change
A suite that only runs on a developer's laptop is a suite that gets skipped under deadline pressure. Wire it into Jenkins (or any CI tool) so it runs automatically on every RecipeBox pull request — see the CI Basics with Jenkins lesson.
6. Keep Test Data Separate from Test Logic
Hard-coded strings scattered through test methods make the suite brittle and hard to extend. Pull test data out into an Excel sheet or CSV file, as covered in Reading Test Data from Excel/CSV — adding a 21st RecipeBox test case should mean adding a spreadsheet row, not editing Java code.
7. Take a Screenshot on Every Failure, Automatically
A failure message tells you what assertion failed. A screenshot at the moment of failure often tells you why — a modal covering the button, an error banner nobody expected, a blank page. Wire it into a TestNG listener so it happens for every test with zero effort per test method:
public class ScreenshotListener implements ITestListener {
@Override
public void onTestFailure(ITestResult result) {
WebDriver driver = BaseTest.getDriverFromContext(result);
TakesScreenshot ts = (TakesScreenshot) driver;
File source = ts.getScreenshotAs(OutputType.FILE);
try {
String fileName = result.getName() + "_" + System.currentTimeMillis() + ".png";
FileUtils.copyFile(source, new File("test-output/screenshots/" + fileName));
} catch (IOException e) {
e.printStackTrace();
}
}
}
Register the listener once, either in testng.xml or with an annotation:
@Listeners(ScreenshotListener.class)
public class AddRecipeTest extends BaseTest {
// every @Test in this class now auto-captures a screenshot on failure
}
The Full Checklist
| Practice | Why it matters |
|---|---|
| Explicit waits, never Thread.sleep() | Faster and far less flaky than a fixed guess |
| Locators live in Page Objects only | One place to fix when the UI changes |
| Every test is independent | Safe to reorder, retry, or run in parallel |
| Assertions carry a real message | Failures are debuggable from the log alone |
| Suite runs in CI on every change | Regressions get caught before merge, not after |
| Test data lives outside the code | Non-developers can extend coverage safely |
| Automatic screenshot on failure | Turns "it failed" into "here's why it failed" |