You're reading for free via Shivam Bharadwaj's Friend Link. Become a member to access the best of Medium.
Member-only story
Reasons To Keep Assertions Out Of Page Object Models
The Power of Clean Code: Why Assertions Belong Outside POMs

In the Page Object Model (POM) pattern, the goal is to keep user interactions (clicking buttons, filling forms, etc.) separate from test assertions or validations. Page objects should only interact with web page elements, while the test files handle all the assertions. This ensures better separation of concerns, maintainability, and reusability of the code.
If you are behind the Medium paywall and can’t read this article, click here this publication is open to everyone.
Why This Approach is Important
- POM handles interactions, while the test files handle assertions and test logic.
- Keeping assertions in the test files allows easier updates when logic changes.
- A single Page Object can be reused across different tests without embedding test-specific logic.
- Test logic and assertions are centralized, making it easier to understand what the test is validating.
Example Without Assertions in Playwright POM
- Page Object Model (POM) — No Assertions
// loginPage.ts - POM for the Login Page using Playwright
import { Page } from '@playwright/test';
export class LoginPage {
// Define page variable
private page: Page;
// Constructor to initialize the page
constructor(page: Page) {
this.page = page;
}
// Locators
private usernameField = '#username';
private passwordField = '#password';
private loginButton = '#login';
private errorMessage = '#error';
// Actions: Interact with the page without any assertions
async enterUsername(username: string) {
await this.page.fill(this.usernameField, username);
}
async enterPassword(password: string) {
await this.page.fill(this.passwordField, password);
}
async clickLoginButton() {
await this.page.click(this.loginButton);
}
// Function to get error message text
async getErrorMessageText(): Promise<string> {
return this.page.textContent(this.errorMessage);
}
}
In this LoginPage POM, we only define actions for interacting with the page (e.g., filling fields, clicking buttons). Importantly, no assertions are made within the page object.
Test File — Assertions in the Test Using Playwright
// login.spec.ts - Test file using the LoginPage POM with Playwright
import { test, expect } from '@playwright/test';
import { LoginPage } from './loginPage';
test('Should show an error message for incorrect login', async ({ page }) => {
const loginPage = new LoginPage(page);
// Interacting with the page through POM
await loginPage.enterUsername('invalidUser');
await loginPage.enterPassword('invalidPassword');
await loginPage.clickLoginButton();
// Assertion is handled in the test file
const errorMessage = await loginPage.getErrorMessageText();
await expect(errorMessage).toHaveText('Invalid credentials');
});ty
Here, the test file handles the assertion (await expect(errorMessage).toHaveText('Invalid credentials')
). This ensures that the POM remains focused on page interaction, while the test handles validation and logic.
Why No Assertions in POM?
Let’s explore why embedding assertions in a POM is a bad practice.
- Bad Practice Example — Assertions in POM (Playwright)
// loginPage.ts - Bad Practice: Assertions in POM
import { expect, Page } from '@playwright/test';
export class LoginPage {
private page: Page;
constructor(page: Page) {
this.page = page;
}
private usernameField = '#username';
private passwordField = '#password';
private loginButton = '#login';
private errorMessage = '#error';
// Interactions remain the same
async enterUsername(username: string) {
await this.page.fill(this.usernameField, username);
}
async enterPassword(password: string) {
await this.page.fill(this.passwordField, password);
}
async clickLoginButton() {
await this.page.click(this.loginButton);
}
// Bad Practice: Assertion inside the POM
async assertErrorMessage(expectedMessage: string) {
const actualMessage = await this.page.textContent(this.errorMessage);
expect(actualMessage).toBe(expectedMessage); // Assertion in POM (bad practice)
}
}
In this case, the assertErrorMessage
method performs an assertion inside the POM, which is not ideal because:
- Mixes Concerns: It merges page interaction and validation.
- Less Re-usability: Any change to error message validation requires changes in the POM itself.
- Harder to Maintain: Scattering assertions across POMs and tests leads to duplicated and harder-to-maintain code.
Bad Practice Test Example
// login.spec.ts - Bad practice test file
import { test } from '@playwright/test';
import { LoginPage } from './loginPage';
test('Should show an error message for incorrect login', async ({ page }) => {
const loginPage = new LoginPage(page);
await loginPage.enterUsername('invalidUser');
await loginPage.enterPassword('invalidPassword');
await loginPage.clickLoginButton();
// Assertion is done in the POM (bad practice)
await loginPage.assertErrorMessage('Invalid credentials');
});
Problems with This Approach:
- Assertions should be kept in the test files, but here they are in the POM, mixing responsibilities.
- If multiple test cases need different assertions, the POM has to be updated.
- If validation logic changes, you must change both the POM and test files.
Best Practice Summary
- Page Object Models (POM): Focus on interacting with page elements and do not contain assertions.
- Test Files: Handle all assertions and validations. Keep your test logic separate from page interactions.
By maintaining this separation, you enhance the reusability, maintainability, and readability of your test code.
Conclusion
This structure ensures that both your test logic and page interaction remain organized, clean, and easy to maintain.
If you enjoyed this story, please click the 👏 button and share to help others find it! Feel free to leave a comment below.
Want the latest articles delivered straight to your inbox? Subscribe now and never miss out!
“If you enjoyed this article and found it helpful, consider buying me a coffee ☕️. Your support helps me keep creating content that adds value to our community. Thanks a ton!”