Locators and Assertions Interview Questions for Playwright Testing
38 Locators and Assertions questions with answers for playwright testing roles — 12 easy, 20 medium, 6 hard.
About Locators and Assertions
Covers role, label, text and test id locators, strictness, filtering and chaining, and web-first assertions such as toBeVisible, toHaveText, toHaveCount, expect.poll and toPass. Good locators and meaningful assertions decide whether a suite is stable or flaky, so interviewers ask many practical questions on choosing and fixing them.
Sheet progressStart practicing
3 QUESTIONS FREESign up to unlock the full question bank and track your progress.Unlock every question and answer, then continue practising company-wise and package-wise.
getByRole finds elements by their ARIA role and accessible name, the same way screen readers and users see the page. For example getByRole('button', { name: 'Save' }) keeps working when CSS classes or DOM structure change. It also pushes the team towards accessible markup. The trade-off is that badly built components without proper roles may need a test id instead.
03
What is the difference between getByText and getByLabel?
getByText matches visible text anywhere on the page, so it suits paragraphs, links and messages. getByLabel finds a form control through its associated label element, aria-label or aria-labelledby, so it is the right choice for inputs such as Email or Password. Using getByLabel for inputs also checks that labels are wired correctly, which getByText cannot do.
Use getByTestId when no user-facing locator is stable or unique, for example icon-only buttons, dynamic grids or canvas wrappers. It matches the data-testid attribute by default, and you can change the attribute name with testIdAttribute in the config. Test ids are very stable but users never see them, so prefer role, label or text first and keep test ids as a fallback.
05
What is the difference between toHaveText and toContainText?
toHaveText checks that the element's full text matches the expected string or regex, after normalising whitespace. toContainText passes if the expected text appears anywhere inside it. Use toHaveText for exact labels like a heading, and toContainText for longer messages where only part matters. Both are web-first assertions, so they retry until the timeout before failing.
toBeAttached only checks that the element exists in the DOM. toBeVisible also needs it to have a non-empty bounding box and no visibility:hidden style. A hidden modal can be attached but not visible. Use toBeVisible for anything a user should see, and toBeAttached for hidden inputs or elements whose presence matters more than appearance.
// waits until exactly 5 data rows are rendered
const rows = page.getByRole('table').getByRole('row');
await expect(rows).toHaveCount(6); // 1 header + 5 rows
// avoid: expect(await rows.count()).toBe(6)
// that reads once and does not retry
08
Write code to fill a login form using label-based locators.
Web-first assertions wait 5 seconds by default. Change it globally in playwright.config with expect: { timeout: 10000 }, or for one assertion with await expect(locator).toBeVisible({ timeout: 15000 }). Raise it only for genuinely slow steps like report generation. Raising it everywhere hides performance problems and makes failing tests slower to report.
10
What is the difference between await expect(locator) and a plain expect on a value?
await expect(locator).toHaveText('Paid') is a web-first assertion: it retries until the element matches or the timeout ends. expect(value).toBe('Paid') is a generic assertion that checks a value once, immediately. Use web-first assertions for anything on the page and generic ones for plain data such as an API JSON field you already have.
11
Tell me about a time you replaced brittle locators in an existing suite.
Situation: a suite I joined used long XPath selectors and about ten tests broke after every UI release. Task: make them stable. Action: I rewrote the locators with getByRole and getByLabel, added test ids for two custom grids and moved everything into page objects. Result: locator-related failures dropped to almost zero over the next three releases.
12
A multi-select filter test passes even when an extra option stays selected. How do you tighten it?
The test probably checks only that the wanted options are selected, for example with a loop of toBeChecked or a contains check. Replace it with await expect(select).toHaveValues(['red', 'green']), which passes only when exactly those values are selected, and it retries until the timeout. For custom multi-select widgets, assert the full list of selected chips with toHaveText on the chip locator.
13
What is the difference between waitForSelector and expect with locator?
Locators are strict: an action such as click or fill throws a strict mode violation if the locator matches more than one element. This prevents a test from silently clicking the wrong item. Fix it by making the locator more specific with a name, filter or chaining. Methods like count(), all() or first() are allowed to work with many matches on purpose.
19
How do you filter a list of locators by text or by a child element?
expect.poll calls a function again and again until the value it returns passes the matcher or the timeout runs out. It is useful for values that are not locators, such as an API status or a database count. Example: await expect.poll(async () => (await request.get('/api/job/7')).status()).toBe(200). You can tune the intervals and the timeout.
21
A locator matches three Delete buttons and the test fails with a strict mode violation. How do you fix it?
First scope the locator to the item you mean, for example the row that contains the user's name: page.getByRole('row', { name: 'Ravi Kumar' }).getByRole('button', { name: 'Delete' }). Do not reach for first() or nth() as a quick fix, because the order can change and the test may delete the wrong record. Scoping by meaningful context is stable and readable.
22
Your test clicks a button before the page's JavaScript has attached its handler, so nothing happens. What do you do?
This is a hydration problem: the button is visible and enabled but not yet interactive. Wait for a signal that the app is ready, such as a loaded data request or a visible element that only appears after hydration, then click. A better fix is to report it to developers, because real users hit the same issue. Avoid fixed waitForTimeout calls.
and() returns a locator that matches elements satisfying both locators, for example a button that also has a certain title. or() matches either locator, which helps when the UI may show one of two states, such as a New email button or a security dialog. With or(), wrap the assertion carefully, because if both appear the strict check fails.
24
How do you assert an element's CSS class, attribute or value?
They pick one element by position from a locator that matches many. They are fine when order is truly meaningful, such as the first search result. They are risky when order depends on data, sorting or rendering speed, because the test may silently act on the wrong element. Prefer filtering by text or a child element that identifies the item.
26
A toast message disappears after two seconds and the assertion on it is flaky. How do you test it reliably?
Assert immediately after the action that triggers it, using a web-first assertion like toBeVisible and toHaveText, which start checking at once. Do not add sleeps before the check. If the toast is still missed, control time with page.clock so it does not auto-hide, or verify the result through the data it confirms, such as the saved record.
27
How do you locate elements inside a specific row or card?
Chain locators: find the container first, then search inside it. Example: page.getByRole('row', { name: 'INV-104' }).getByRole('button', { name: 'Download' }). Each step narrows the search area, so short names like Edit or Download stay unique. Chaining also documents intent clearly and avoids long CSS paths that break when markup changes.
28
How do you verify a page's URL and title after navigation?
await page.getByRole('link', { name: 'Orders' }).click();
await expect(page).toHaveURL(/\/orders$/);
await expect(page).toHaveTitle('My Orders | Shop');
// both retry, so no need to wait for navigation manually
29
Describe a time a test passed but the feature was actually broken.
Situation: a checkout test passed, but users saw a blank order summary. Task: find why. Action: I found the test only asserted that the summary container was visible, not its contents. I added assertions on item names, totals and the order id. Result: the test caught the regression, and I reviewed other tests for similar weak assertions.
30
How do you balance pressure to automate quickly against writing stable tests?
Situation: a manager wanted 50 tests for a release in one week. Task: deliver without creating a flaky suite. Action: I agreed on the 20 highest-risk flows first, used stable locators and shared fixtures, and listed the rest as follow-ups. Result: the 20 tests ran green in CI from day one and the rest were added next sprint.
31
A test must confirm that an error banner does not appear, but it always waits the full timeout. How do you fix it?
Use a web-first negative assertion such as await expect(banner).toBeHidden() or not.toBeVisible(); they pass as soon as the element is absent, so they do not wait the full timeout. The real problem is timing: assert after a positive signal, such as the success message appearing, otherwise the banner may simply not have rendered yet and the check passes too early.
32
Describe a time you convinced developers to add accessible labels or test ids.
Situation: many inputs had no labels, so our tests relied on long CSS selectors that broke often. Task: get stable hooks without slowing developers. Action: I listed the worst screens, showed how getByLabel tests read and how the same labels help screen reader users, and offered to raise small pull requests myself. Result: the team added labels to new work as a rule and locator breakages dropped sharply.
33
How would you handle a very flaky element that appears after an animation?
toPass retries a whole block of code, including several actions and assertions, until it runs without throwing. expect.poll retries only one function that returns a value. Use toPass for flows like 'click refresh, then check the status' that must be repeated together. It has no timeout by default, so always pass a timeout to avoid a hung test.
35
How do you design a locator strategy for a team so tests survive UI redesigns?
Set a written priority order: role with name, then label, placeholder or text, then test id, and CSS or XPath only as a last resort. Agree with developers to add data-testid on complex widgets. Keep locators inside page objects so a change is fixed in one place. Add a lint rule or review check against long CSS chains and nth-child selectors.
36
Developers keep changing CSS class names and 40 tests break every sprint. How do you fix this long term?
Replace class-based selectors with role, label and text locators, and agree on data-testid for widgets without good semantics. Move all locators into page objects so future changes touch one file. Add a code review rule and a lint check that flags raw CSS selectors. Share a short locator guide with developers so they know which attributes tests depend on.
37
How do you write a custom matcher with expect.extend?
expect.extend takes an object of matcher functions. Each receives the received value and arguments, and returns { pass, message, name, expected, actual }. For example a toHaveAmount matcher can read a locator's text, strip the currency symbol and compare numbers, retrying with expect.poll inside. Export the extended expect from a shared fixtures file so all tests use the same version.
38
A test checks a price that is formatted differently per locale. How do you make the assertion robust?
Fix the locale for the test with the locale and timezoneId options so the format is predictable. Build the expected string with Intl.NumberFormat using the same locale, rather than hard-coding it. If you test several locales, run them as projects or parameterised tests. For business logic, also assert the raw number through the API response.
Frequently asked questions
How important is Locators and Assertions in Playwright Testing interviews?
Covers role, label, text and test id locators, strictness, filtering and chaining, and web-first assertions such as toBeVisible, toHaveText, toHaveCount, expect.poll and toPass. Good locators and meaningful assertions decide whether a suite is stable or flaky, so interviewers ask many practical questions on choosing and fixing them.
How should I use the Locators and Assertions sheet?
Go top to bottom — easy to hard. Answer before opening each answer, tick what you solved and save misses to Last-Minute Revision.