A developer wants to keep their tests "dry," so they put the expect statement inside the Page Object method. It makes sense at first. login() handles everything: fill the form, click the button, confirm you’re on the dashboard. One method. Clean. ✅
Until you need to test a failed login. ✖️
Now your login() method asserts success but your negative test is looking for a failure. To fix it, you add loginExpectingFailure(). Then loginAndCheckError(). Six login methods later, your POM is a graveyard of duplicated logic.
This is the exact trap teams fall into when a suite grows.
Page Objects should be simple. They store locators and perform actions. That is it. Assertions belong in the test file, where the intent of the test is immediately visible.
The Clean Pattern
Instead of baking the assertion into the class, keep the class "pure":
class LoginPage {
async login(user, pass) {
await this.emailInput.fill(user);
await this.passInput.fill(pass);
await this.submitBtn.click();
}
}
Then, handle the different outcomes in your actual test files:
// Happy path
test('success', async ({ page, loginPage }) => {
await loginPage.login('user@test.com', 'pass123');
await expect(page).toHaveURL('/dashboard');
});
// Negative path
test('failure', async ({ page, loginPage }) => {
await loginPage.login('user@test.com', 'wrong');
await expect(loginPage.error).toBeVisible();
});
-
One method: A single
login()method with zero assertions. - Visible Intent: Anyone reading the test knows exactly what is being verified.
-
No Bloat: No duplicated methods. No boolean flags. No
loginVersion4().
Do you enforce a "no assertions in POM" rule on your team, or is it a free-for-all? Drop it in the comments.
Top comments (0)