lecture 4 of 160 completed

The Page Object Model

Selenium teams live or die by this pattern. It keeps locators in one place so a UI change is one edit.

These lines appear in eight different test files:

await driver.findElement(By.css('[data-testid="contact-name-input"]')).sendKeys('Dana');
await driver.findElement(By.css('[data-testid="contact-email-input"]')).sendKeys('dana@foxlabs.io');
await driver.findElement(By.css('[data-testid="save-contact"]')).click();

A developer renames one test id. You search the project and fix eight files. Miss one and a test fails tomorrow for a reason that has nothing to do with the feature.

A developer once added a "Remember me" checkbox to our login page. Fifteen minutes of work for him. It took me most of a day to fix the tests, because the login steps were copied into thirty files.

The answer in Selenium is the Page Object Model: one class per screen, holding the locators and the actions for that screen.

class ContactForm {
  constructor(driver) {
    this.driver = driver;
    this.nameInput = By.css('[data-testid="contact-name-input"]');
    this.emailInput = By.css('[data-testid="contact-email-input"]');
    this.saveButton = By.css('[data-testid="save-contact"]');
  }

  async fill({ name, email }) {
    await this.driver.findElement(this.nameInput).sendKeys(name);
    await this.driver.findElement(this.emailInput).sendKeys(email);
    await this.driver.findElement(this.saveButton).click();
  }
}

Notice that the locators are stored as By objects, not as found elements. This matters. If you find the elements in the constructor, they go stale the moment the page changes, and you get the error from the last lecture. Store the locator and find the element when you use it.

Your test now reads clearly:

const form = new ContactForm(driver);
await form.fill({ name: 'Dana Fox', email: 'dana@foxlabs.io' });

You will be asked about this pattern in interviews. The short answer they want: it keeps locators and page actions in one place, so a UI change is one edit instead of many.

Now the mistake almost everyone makes. Do not put your assertions inside the page object. It looks tidy, but then nobody can read a test and see what it proves without opening another file. The page object holds locators and actions. The test holds the assertions.

Remember this: store locators, not elements. Keep assertions in the test.

Reference: Page object models.