Hello everyone! I was inspired to write this article by the fact that there’s no official documentation on implementation in . You might find some articles online, but I find them imperfect - I’ve got a thing or two to add. Page Object Model (POM) Cypress I also suggest that you’ve already searched online with keywords and read . Don’t fool yourself that this is the canon usage just because the article is posted in (at least scroll down to the comments section and you’ll see what I mean). is still the most popular test automation design pattern and it works pretty well with ! cypress page object model the article that says not to use this design pattern Cypress Cypress blog POM Cypress WHAT’S POM? is a test automation programming (or design) pattern. It’s built on top of the (object-oriented programming). Page Object Model (POM) Inheritance paradigm of OOP As it’s a and it’s not unique for web automation tool, no wonder there are no official docs for that ☝️ design pattern every Cypress Key concepts of are: POM Some pages have mutual logic This logic may be shared in a single place to avoid code repetition ( to ) parent class child classes Pages have and elements methods Element = DOM tree element Method = sequence of actions on the page Pages are presented as , elements as , and methods as classes class attributes class methods IMPLEMENTATION Here’s an with tests for . example GitHub repo Swag Labs example web app Create a folder for your , preferably name it and place it in your directory, like this: Page Objects pages cypress Create inside of folder - that’ll be a parent page (class) for all other pages. It’ll contain selectors/methods that can be used on page of the app. page.js pages EVERY Create a class inside of and fill it with some basic stuff. No worries if there’s not much content in the beginning - you’ll fill it up later! Page page.js class Page { open(path) { return cy.visit(path) } } export default Page Create right near and fill it with an class by extending existing class: auth.page.js page.js AuthPage Page import Page from './page' class AuthPage extends Page { get inputUsername() {return cy.get('[data-test="username"]')} get inputPassword() {return cy.get('[data-test="password"]')} get buttonLogIn() {return cy.get('[data-test="login-button"]')} get containerError() {return cy.get('[data-test="error"]')} open() { return super.open('/') } logIn(username, password) { this.inputUsername.type(username) this.inputPassword.type(password) this.buttonLogIn.click() } } export default new AuthPage() Now you simply import the page that you need in your ( or ) files and call its selectors/methods: test spec cy import AuthPage from '../pages/auth.page' import user from '../fixtures/user.json' import error from '../fixtures/error.json' describe('Authentication', () => { beforeEach(() => { AuthPage.open() }) it('With existing credentials', () => { AuthPage.logIn(user.username, user.password) cy.location('pathname') .should('include', 'inventory') }) it('With non-existing credentials', () => { AuthPage.logIn('foo', 'bar') AuthPage.containerError .should('have.text', error.credentials) }) }) And that’s pretty much it ☝️ Now compare it with the raw approach, when you don’t use : POM import user from '../fixtures/user.json' import error from '../fixtures/error.json' describe('Authentication', () => { beforeEach(() => { cy.visit('/') }) it('With existing credentials', () => { cy.get('[data-test="username"]') .type(user.username) cy.get('[data-test="password"]') .type(user.password) cy.get('[data-test="login-button"]') .click() cy.location('pathname') .should('include', 'inventory') }) it('With non-existing credentials', () => { cy.get('[data-test="username"]') .type('foo') cy.get('[data-test="password"]') .type('bar') cy.get('[data-test="login-button"]') .click() cy.get('[data-test="error"]') .should('have.text', error.credentials) }) }) I think it’s straightforward that lets you get rid of repetitive code blocks and make tests much more readable. POM BEST PRACTICES Method creation for every action It doesn’t make any sense if you create methods for simple actions - you’re just writing more code and making larger, which can’t be good. Page Objects create methods for simple actions (i.e. click, type text, etc): ❌ DO NOT LoginPage.clickLogInButton() ✅ instead: USE THIS LoginPage.buttonLogIn.click() Huge base page class You should only add those & to the parent page, which are related to child pages. selectors methods ALL If there’s a logic that’s not related to any page, consider using . Cypress Custom Commands If you have multiple pages that share the same elements/methods but you can’t group them under a single parent class - you may create a for such logic and include it in pages where it’s required (but it’s a topic for another article). Page Element