Every test case has preconditions, steps, and cleanup — your templates enforce it.
JUnit annotations are that template: @BeforeEach is preconditions, @Test is steps+expected, @AfterEach is cleanup — guaranteed to run even on failure.
You've written tests with browser setup and quit() repeated inside each one — and if an assertion fails, quit() never runs, leaking a browser. JUnit's lifecycle annotations fix both, and they're the skeleton of every professional suite:
import org.junit.jupiter.api.*; import org.openqa.selenium.WebDriver; import org.openqa.selenium.chrome.ChromeDriver; public class BaseTest { protected WebDriver driver; // protected = subclasses can use it @BeforeEach // runs before EVERY test method void setUp() { driver = new ChromeDriver(); } @AfterEach // runs after every test — EVEN failed ones void tearDown() { if (driver != null) driver.quit(); } }
public class LoginTest extends BaseTest { // inheritance, from the OOP lesson @Test @DisplayName("Valid user lands on Products page") void validLogin() { LoginPage login = new LoginPage(driver); login.open(); login.loginAs("standard_user", "secret_sauce"); assertEquals("Products", new ProductsPage(driver).getTitle()); } @Test @DisplayName("Locked-out user sees an error") void lockedOutLogin() { LoginPage login = new LoginPage(driver); login.open(); login.loginAs("locked_out_user", "secret_sauce"); assertTrue(login.getErrorMessage().contains("locked out")); } }
[INFO] Running LoginTest [INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0 [INFO] BUILD SUCCESS
Each test gets a fresh browser (no state leaking between tests — the silent killer of reliability), and teardown is guaranteed. @DisplayName makes reports readable by humans; write them like test case titles.
assertEquals(expected, actual); // the workhorse assertEquals(expected, actual, "message shown on failure"); assertTrue(condition); assertFalse(condition); assertNotNull(object); assertAll( // check several, report ALL failures () -> assertEquals("Products", title), () -> assertEquals("1", cartCount) );
(expected, actual). Swap them and failure messages lie to you: "expected <the bug> but was <the truth>". Drill it now.Fix: Almost always the wrong import: org.junit.Test is JUnit 4 — Surefire is looking for JUnit 5's org.junit.jupiter.api.Test. Also check the class name ends in Test and lives under src/test/java.
Fix: Class-level fixtures run before any instance exists, so they must be static. If you need instance state, you wanted @BeforeEach.
Fix: setUp() never ran — usually the same JUnit 4 import problem on @BeforeEach, or your class stopped extending BaseTest during the refactor.
BaseTest as above; make your existing test classes extend it and delete their per-test driver code.assertEquals("2", cartCount, "Cart should show 2 after adding two items") — run and see how the message appears in output. Then fix or remove it.