Playwright · Intermediate

Playwright Waiting, Network and Fixture Interview Questions

Where auto-waiting stops helping, watching real requests, mocking, and custom fixtures.

20 questions, each with the answer and the reason every other option is wrong. You can read them here, then sit the timed session to find out what you actually remember.

1. const text = await page.getByTestId('count').textContent(); expect(text).toBe('1'); Why is this less reliable than expect(locator).toHaveText('1')?

Answer

textContent() reads once and the plain expect does not retry, so a value that updates a moment later fails

Correct. Pulling the value into a variable removes the retrying behaviour. This is the most common mistake among people new to Playwright.

If the value comes from a locator inside expect, it retries. Pull it into a variable first and it does not.

Why the other answers are wrong

  • textContent() is deprecated. It is current API and useful when you genuinely need the raw string.
  • They behave identically. One retries and one does not. That difference decides whether the test is stable.
  • toBe cannot compare strings. It compares strings fine. The problem is the missing retry.

2. You need to check the data your app sent when saving a contact. What is the correct order?

Answer

Start waiting for the response, then click, then await the promise

Correct. const p = page.waitForResponse(...); await click(); const res = await p; If you click first, the response may already have arrived.

Set up the wait before the action that triggers the request. Order matters here and interviewers ask about it.

Why the other answers are wrong

  • Click, then call waitForResponse. The response can arrive before you start waiting, and the test hangs until timeout.
  • Use waitForTimeout after the click, then read the last response. A fixed wait is a guess, and there is no reliable 'last response' to read.
  • Network data cannot be checked in Playwright. waitForRequest and waitForResponse exist for exactly this.

3. What does page.route with route.fulfill let you do?

Answer

Answer a request yourself with fixed data, so the real server is never called

Correct. This is mocking. It makes hard states like server errors easy to test.

route.fulfill returns your own response. Set the route up before page.goto or the page loads first.

Why the other answers are wrong

  • Speed up the real network. It replaces the response; it does not change real network speed.
  • Record requests for later replay only. Recording is a different feature. fulfill answers the request directly.
  • Block the browser from loading the page. It intercepts matching requests, not the whole page.

4. What is the main risk of mocking most of your API responses?

Answer

The tests stop checking the real server, so an API change can break the app while tests stay green

Correct. Keep at least one path that reaches the real API, or a contract change will pass unnoticed.

Mock the hard states; keep one real path so a renamed field cannot pass your suite.

Why the other answers are wrong

  • Mocked tests run more slowly. They usually run faster. Speed is not the risk.
  • Mocking is unreliable and flaky. Mocks are very stable. That stability is what hides the problem.
  • Playwright cannot mock POST requests. It can mock any method.

5. In a custom fixture, what runs after the use() call?

Answer

Cleanup, it runs after the test finishes, including when the test failed

Correct. Code before use() is setup, code after it is teardown. That guarantee is why fixtures beat manual setup.

Fixtures give a test what it needs and clean up afterwards, even on failure.

Why the other answers are wrong

  • Nothing; use() is the last statement. Anything after use() is the teardown phase.
  • It runs before the test. Setup is before use(); teardown is after.
  • It runs only if the test passed. Teardown runs either way, which is the point.

6. Why should a login fixture assert that login succeeded before calling use()?

Answer

Otherwise a broken login makes many tests fail with confusing errors about missing elements

Correct. An assertion in setup turns thirty confusing failures into one clear message: login did not work.

Always check that setup worked. A failure in setup should say so plainly.

Why the other answers are wrong

  • Playwright requires an assertion in every fixture. It does not require one. It is a practice that saves debugging time.
  • It makes the fixture run faster. It adds a check; the value is clearer failures.
  • Without it the session is not saved. Saving state is separate from asserting the login worked.

7. What does storageState do?

Answer

Saves cookies and local storage to a file so later tests start already logged in

Correct. Log in once in a setup project, save the file, and every test loads it into a fresh context.

Tests share a saved copy of the login, not a live session, so they stay independent and still start fast.

Why the other answers are wrong

  • Keeps one browser session alive across all tests. No live session is shared. Each test gets a fresh context with saved state loaded in.
  • Caches page HTML for faster loading. It stores authentication state, not page content.
  • Replaces the need for fixtures. It solves login cost; fixtures solve setup and cleanup generally.

8. Your suite passes with one worker and fails with four. What is the most likely cause?

Answer

Tests share data or an account, and now they collide because they run at the same time

Correct. Parallel execution exposes shared state. Give each test its own data with a unique value.

Parallel runs do not create dependency bugs. They reveal the ones you already had.

Why the other answers are wrong

  • Playwright has a bug in parallel mode. Parallel mode is well tested. It reveals dependencies rather than creating them.
  • Four workers is always too many. The worker count is not the problem; the shared state is.
  • Traces cannot be recorded in parallel. They can. This is unrelated to the failures.

9. Where should assertions live when you use page objects?

Answer

In the test, not in the page object

Correct. Page objects hold locators and actions. Assertions in the page object hide what a test proves behind another file.

If you cannot tell what a test proves without opening a second file, the split has gone too far.

Why the other answers are wrong

  • In the page object, so they can be reused. Reuse is not worth losing the ability to read a test and see what it checks.
  • In a separate assertions file. That adds a layer and moves the checks even further from the test.
  • In beforeEach. Setup checks belong there, but a test's own checks belong in the test.

10. A test creates a contact named 'Dana Fox'. Another test does the same. What breaks and when?

Answer

The second test finds two matching rows once both have run, and the failure moves around under parallel runs

Correct. Shared data is the classic cause of failures that cannot be reproduced. Add a unique value like Date.now() to the name.

Give each test data no other test uses. Unique names remove a whole class of unreproducible failures.

Why the other answers are wrong

  • Nothing; Playwright isolates data automatically. Playwright isolates the browser, not your application's database.
  • Both tests fail immediately. They pass alone. That is what makes this hard to diagnose.
  • Only the first test fails. Usually the second one fails, and which one it is changes with timing.

11. What is the practical test for whether a suite is independent?

Answer

Run any single test on its own, and run the suite in a different order. Everything must still pass

Correct. If a test only passes after another one ran, it is not finished.

Run one alone, or reverse the order. Independent tests pass either way.

Why the other answers are wrong

  • Check that the suite passes in its normal order. Passing in order hides dependencies. Running one alone exposes them.
  • Count the beforeEach hooks. The count proves nothing on its own.
  • Make sure all tests are in one file. File layout does not create or fix independence.

12. What is the first thing to open when a test failed on CI and you cannot reproduce it locally?

Answer

The trace, which shows every action with a snapshot of the page at that moment

Correct. npx playwright show-trace trace.zip is usually faster than any amount of local re-running.

Traces are Playwright's strongest debugging feature. Turn them on for retries and open them first.

Why the other answers are wrong

  • The application logs. Sometimes useful later, but the trace shows the failing moment directly.
  • The test code. You will read it, but the trace tells you what the page actually looked like.
  • The CI machine configuration. Rarely the cause, and the trace will point you there if it is.

13. trace: 'on-first-retry' records a trace when?

Answer

Only when a test failed and is being retried, which is exactly the case you cannot reproduce

Correct. It keeps the storage cost low while capturing the runs you actually need to investigate.

on-first-retry is the setting most teams want: evidence for the failures, no cost for the passes.

Why the other answers are wrong

  • For every test, always. That is trace: 'on', which produces very large artifacts.
  • Never in CI. It works in CI, and that is where it matters most.
  • Only when you run with --debug. It is a config option, independent of debug mode.

14. Which check proves a filter narrowed a table, rather than that one row exists?

Answer

await expect(page.getByTestId('contact-row')).toHaveCount(1)

Correct. The count is what separates a working filter from one that was ignored.

A filter is defined by what it removes. Assert the size of the result.

Why the other answers are wrong

  • await expect(page.getByText('Maya Chen')).toBeVisible(). Maya is visible whether or not the other rows were removed.
  • await expect(page.getByTestId('search-input')).toHaveValue('Maya'). That checks what you typed, not what the table did.
  • Checking that the table element exists. The table exists in both cases.

15. After a failed payment, why assert toBeHidden on the confirmation as well as checking the error?

Answer

An app can show an error and still complete the action; checking only the error would miss that

Correct. Assert that the error appeared and that the forbidden thing did not happen.

Negative cases need two checks: the error is shown, and the action did not go through.

Why the other answers are wrong

  • It makes the test run faster. It adds a check. The value is catching a real class of bug.
  • Playwright requires two assertions per test. There is no such requirement.
  • toBeHidden is needed to reset the page. Assertions never change the page.

16. What does test.step add to a test?

Answer

Named groups of actions that appear in the report and trace, making long tests readable

Correct. Steps show up in the HTML report, so a failure points at the phase that broke.

In a long end-to-end flow, steps turn a wall of actions into a readable report.

Why the other answers are wrong

  • Retry behaviour for that block. Steps are for reporting, not retrying.
  • Parallel execution of the block. Steps run in order like normal code.
  • Automatic assertions. Steps group code; they assert nothing.

17. Why create test data through request rather than by filling the UI form?

Answer

It takes milliseconds instead of seconds, and a change to the form cannot break your setup

Correct. Set up through the API; spend the test's time on the behaviour you are actually checking.

Setup through the API is faster and does not break when the form changes.

Why the other answers are wrong

  • API setup checks more of the app. It checks less. Speed and stability are the reasons, not coverage.
  • Playwright cannot fill forms reliably. It fills forms very reliably. This is about cost.
  • It removes the need for assertions. Assertions are unrelated to how you set up data.

18. Two locators must both be true at the same moment. What is the safest approach?

Answer

Assert on a single element or state that represents both, because the page can change between two separate reads

Correct. Separate assertions are separate moments in time. When simultaneity matters, assert one thing.

Two assertions are two moments. If they must hold together, find one thing to assert.

Why the other answers are wrong

  • Read both into variables and compare them. That is the least safe option: two reads, no retrying.
  • Add a waitForTimeout between them. A fixed wait does not make two reads simultaneous.
  • It is impossible to check. You express it as one condition instead of two.

19. Where should the storageState auth file be kept?

Answer

Outside version control, for example in a gitignored playwright/.auth folder

Correct. The file contains a real session. Committing it leaks a login.

Treat the auth state file as a credential. Generate it in setup and never commit it.

Why the other answers are wrong

  • Committed to the repository so CI can use it. That leaks credentials. CI should create it in a setup project.
  • In the public folder. That would publish the session to anyone who loads the site.
  • Anywhere; it contains no sensitive data. It contains cookies and tokens, which are sensitive.

20. A test fails only when run with other tests, never alone. Where do you look first?

Answer

Shared state: data with fixed names, a shared account, or a variable outside the tests

Correct. Passing alone and failing together is the signature of a dependency between tests.

Passes alone, fails together means shared state. Give each test its own data and setup.

Why the other answers are wrong

  • The timeout settings. Timeouts would usually affect the test alone as well.
  • The browser version. That would not change based on which other tests ran.
  • The assertion library. Assertions behave the same either way.

Now prove it in code

Knowing the answer and writing the test are different skills. These problems run your test against a working app, then against a copy with the behaviour broken on purpose, and tell you which behaviour your test missed.

More Playwright interview questions

Other frameworks