I have reviewed hundreds of POM implementations. The same mistakes show up in every project. Some are subtle. Some are obvious once you see them. Let me walk you through the top mistakes and how to fix each one.
// BAD -- assertions inside page object
export class LoginPage {
async login(email: string, password: string) {
await this.emailInput.fill(email);
await this.passwordInput.fill(password);
await this.loginButton.click();
// This does NOT belong here!
await expect(this.page).toHaveURL(/dashboard/);
await expect(this.page.getByTestId('welcome')).toBeVisible();
}
}// GOOD -- page object only performs actions
export class LoginPage {
async login(email: string, password: string) {
await this.emailInput.fill(email);
await this.passwordInput.fill(password);
await this.loginButton.click();
}
}
// Assertions stay in the test file
test('login redirects to dashboard', async ({ page }) => {
const loginPage = new LoginPage(page);
await loginPage.goto();
await loginPage.login('john@test.com', 'password123');
await expect(page).toHaveURL(/dashboard/);
await expect(page.getByTestId('welcome')).toBeVisible();
});Why is this bad? Because login() is now only usable for successful logins. If you want to test invalid credentials, login() fails because the assertion fails. Page objects perform actions. Tests verify results. Keep them separate.
A God Object is a page object that does everything. One class with 50 methods covering login, registration, password reset, account settings, and profile management. It violates the single responsibility principle.
// BAD -- test directly uses page object locators
test('login test', async ({ page }) => {
const loginPage = new LoginPage(page);
await loginPage.goto();
// Reaching into the page object for locators
await loginPage.emailInput.fill('john@test.com');
await loginPage.passwordInput.fill('password123');
await loginPage.loginButton.click();
// This defeats the purpose of POM!
});// GOOD -- test uses page object methods
test('login test', async ({ page }) => {
const loginPage = new LoginPage(page);
await loginPage.goto();
await loginPage.login('john@test.com', 'password123');
// Clean. If locators change, only LoginPage changes.
});// BAD -- manual page object creation in every test file
import { LoginPage } from '../pages/login.page';
import { DashboardPage } from '../pages/dashboard.page';
import { TransferPage } from '../pages/transfer.page';
import { ProfilePage } from '../pages/profile.page';
import { SettingsPage } from '../pages/settings.page';
// Same 5 imports and new PageObject(page) in every. single. file.
test('some test', async ({ page }) => {
const loginPage = new LoginPage(page);
const dashboardPage = new DashboardPage(page);
// ...
});Use fixtures. Define page objects once. Inject them as test parameters. No boilerplate imports. No manual construction.
// BAD -- hardcoded credentials in the page object
export class LoginPage {
async loginAsAdmin() {
await this.emailInput.fill('admin@company.com');
await this.passwordInput.fill('Admin@123');
await this.loginButton.click();
}
}// GOOD -- test data in a separate file
export const users = {
admin: { email: 'admin@company.com', password: 'Admin@123' },
customer: { email: 'john@test.com', password: 'password123' },
invalid: { email: 'wrong@test.com', password: 'wrongpass' },
} as const;
// Usage in test:
// await loginPage.login(users.admin.email, users.admin.password);The exception to "no assertions in page objects" is verification helpers like expectLoaded(). A method that checks if the page is ready is acceptable. What you should avoid is embedding test-specific assertions in action methods like login() or addToCart().
Q: What are common mistakes in POM implementation?
A: The top five mistakes I see: 1) Assertions in page objects -- login() should not verify the dashboard loaded. That is the test's job. 2) God Objects -- one class handling login, signup, and settings. Split into focused classes. 3) Tests accessing locators directly instead of using methods -- defeats the purpose of POM. 4) Not using fixtures -- manual page object creation in every file. 5) Hardcoded test data in page objects. I keep actions in page objects, assertions in tests, and test data in separate files.
Key Point: Five common mistakes: assertions in page objects, God objects, exposing locators, skipping fixtures, hardcoded data. Follow the checklist to avoid all five.