I've been using Playwright for a couple of months now, and though I'm certainly no expert, I've learned some tips, tricks and best practices along the way. In this article, we go through some of them, with the aim of helping you write even better E2E tests.
1. Prioritize user-facing attributes
You should use user-facing attributes like text content, accessibility roles and label as much as possible. A user won't know what "id" or "class" means so why should we use them in our tests to find elements? Not only will your tests mimic how your users find elements, but they will also be more robust as user-facing attributes generally change less than ids, class names or other implementation details.
For example, use
await page.locator('text=Login') instead of
await page.locator('#login-button'). A real user will find a button by its text content, not its id, so your tests should too.
Remember, the more your tests resemble the way your software is used, the more confidence they can give you.
2. Use locators over selectors
Using locators will help prevent flakiness or unnoticed breakages when your web page changes. These breakages have the potential to go unnoticed when using standard selectors.
For example, use
await page.locator('text=Login').click() instead of
The main reason locators help mitigate flakiness is down to its level of strictness. There are three possible outcomes when using locators:
Test works as expected.
Selector does not match anything and test breaks loudly.
Multiple elements match the selector (e.g. there is a second "Login" button added to the page somewhere), and the locator complains about this and the test breaks with a nice error message.
This means you don't have to be very thoughtful of selectors, and picking just
text=Login is totally fine - Playwright will do all the heavy lifting to ensure correct and non-flaky testing.
3. Use Page Object Model (POM)
Page Object Model is a common pattern that can help avoid duplication, improve maintainability and simplify interactions between pages in multiple tests.
Writing tests using POM feels more natural as it conveys more intent and encourages behaviour over raw mechanics. Playwright have included this example in their docs to give you an idea on how to implement it.
In saying that, you don't always have to use POM either. Use it when it makes sense to abstract. I often start without POM and only create page object models when I feel that the tests will benefit from it.
Duplication is far cheaper than the wrong abstraction - Sandi Metz.
4. Use double quotes to find specific elements
If you are finding multiple elements with the same partial string, try using double quotes to enable case sensitivity. For example,
await page.locator('text=Checkout') could return two elements as it finds a "Checkout" button and a "Check out this new shoe" heading. Use double quotes if you only want to return the button on its own e.g
await page.locator('text="Checkout"'). See Playwright text selectors for more.
5. Avoid selectors tied to implementation
css can be tied to the DOM structure or implementation. For example:
await page.locator('#tsf > div:nth-child(2) > div.A8SBwf > div.RNNXgb > div > div.a4bIc > input').click();
Pretty gnarly right? These selectors can break when the DOM structure changes, so it's best to avoid relying on them.
That's all the tips I have for you today. If you have any tips or best practices of your own, please share them in the comments below. 🙂
Want to see more?
I mainly write about real tech topics I face in my everyday life as a Frontend Developer. If this appeals to you then feel free to follow me on Twitter: https://twitter.com/cmacdonnacha
Bye for now 👋