Skip to content

test(fixtures): inject Playwright objects through JUnit 5 fixtures - #2017

Merged
Yury Semikhatsky (yury-s) merged 2 commits into
microsoft:mainfrom
yury-s:test-fixtures
Oct 24, 2026
Merged

Yury Semikhatsky (yury-s) merged 2 commits into
microsoft:mainfrom
yury-s:test-fixtures

Conversation

@yury-s

Copy link
Copy Markdown
Member

Summary

  • Replace TestBase and @UsePlaywright in the test suite with a fixture layer in com.microsoft.playwright.fixtures, modeled on upstream browserTest.ts
  • @PlaywrightTest runs each test once per browser; Playwright, Browser, BrowserContext, Page, Server are injected as parameters, each test in a new context
  • @Fixture methods override test- or worker-scoped fixtures; worker objects are pooled so tests run in parallel without sharing Playwright objects across threads
  • @DisabledOnBrowser/@EnabledOnBrowser replace @DisabledIf conditions; @Nested @ParameterizedClass runs a test with different context options

https://claude.ai/code/session_018p7xPf3r5VjAM7V46nzFTM

Replace the TestBase base class and @UsePlaywright with a fixture layer
in com.microsoft.playwright.fixtures modeled on upstream browserTest.ts:

- @PlaywrightTest runs each test once per browser (BROWSER env selects)
- Playwright, Browser, BrowserContext, Page, Server etc. are injected as
  method parameters and created lazily, each test in a new context
- @fixture methods add or override fixtures (test or worker scope)
- worker-scoped objects live in a pool so tests run in parallel without
  sharing Playwright objects across threads
- @DisabledOnBrowser/@EnabledOnBrowser replace @DisabledIf conditions
- @nested @ParameterizedClass runs a test with different context options

Claude-Session: https://claude.ai/code/session_018p7xPf3r5VjAM7V46nzFTM
// Like Playwright Test, do not reuse a worker after a failure: the test may have left the
// browser or the Playwright instance in an unknown state.
boolean failed = context.getExecutionException().filter(e -> !(e instanceof TestAbortedException)).isPresent();
WorkerPool.INSTANCE.release(worker, failed);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

contexts created directly by tests survive worker reuse which breaks context count assertions in later test classes

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in ed346c3. The worker records the contexts its own fixtures create. Before the pool hands a worker to the next test, it closes any other context still open in the worker's browsers. TestWorkerFixtures covers this: the first test leaves a context open and the second asserts that browser.contexts() holds only the worker fixture's context.

try {
entry.definition.close(entry.value);
} catch (Throwable e) {
System.err.println("Failed to close " + entry.definition.describe() + ": " + e);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

swallowing cleanup errors lets tests pass when context or fixture teardown fails

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in ed346c3. Teardown still closes everything, then rethrows the first error with the others attached as suppressed. The test fails (JUnit reports "Failed to close extension context") and its worker is discarded. Errors from closing workers that no test owns, evicted or idle at the end of the run, fail the whole run and name the worker.


@PlaywrightTest
void shouldReuseWorkerFixtures(Browser browser, WorkerResource resource) {
assertSame(browser, resource.browser);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this passes even with a new worker for every test so it does not actually check fixture reuse

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, fixed in ed346c3. TestWorkerFixtures now runs @Isolated and in a fixed order, and asserts that both tests in each browser got the same worker fixture instance and that it was not closed in between. The new TestWorkerPool covers reuse, eviction and discarding deterministically without browsers. Writing this exposed a real problem: the pool kept one worker per test thread while each thread runs every test in all three browsers, so a sequential run relaunched the browser for every invocation. The pool now holds threads × browsers workers.

- close contexts a test leaves open in a worker's browsers before the
  worker is reused, keeping the ones created by worker fixtures
- fail the test when closing its fixtures fails and discard the worker;
  failures to close idle or evicted workers fail the run
- keep a worker per browser for every test thread, since each thread
  runs a test in all browsers in turn, and close idle workers in
  parallel at the end of the run
- check worker reuse with pool unit tests and an isolated integration
  test

Claude-Session: https://claude.ai/code/session_018p7xPf3r5VjAM7V46nzFTM
@yury-s
Yury Semikhatsky (yury-s) merged commit fcbf305 into microsoft:main Oct 24, 2026
19 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants