Skip to content

[Workers] integration testing guide with createTestHarness()#31422

Open
edmundhung wants to merge 4 commits into
productionfrom
edmundhung/test-harness-guide
Open

[Workers] integration testing guide with createTestHarness()#31422
edmundhung wants to merge 4 commits into
productionfrom
edmundhung/test-harness-guide

Conversation

@edmundhung

@edmundhung edmundhung commented Jun 12, 2026

Copy link
Copy Markdown
Member

Summary

Adding API reference and some guides with regards to the new createTestHarness() API, which is now our recommendation for integration tests.

This fixture mentioned in the examples page will be available once cloudflare/workers-sdk#14278 is merged.

Screenshots (optional)

Documentation checklist

  • Is there a changelog entry (guidelines)? If you don't add one for something awesome and new (however small) — how will our customers find out? Changelogs are automatically posted to RSS feeds, the Discord, and X.
  • The change adheres to the documentation style guide.
  • If a larger change - such as adding a new page- an issue has been opened in relation to any incorrect or out of date information that this PR fixes.
  • Files which have changed name or location have been allocated redirects.

@github-actions

Copy link
Copy Markdown
Contributor

This pull request requires reviews from CODEOWNERS as it changes files that match the following patterns:

Pattern Owners
/src/content/changelog/ @cloudflare/pm-changelogs, @cloudflare/product-owners
/src/content/docs/workers/ @cloudflare/workers-docs, @GregBrimble, @irvinebroque, @mikenomitch, @korinne, @WalshyDev, @cloudflare/deploy-config, @cloudflare/product-owners, @cloudflare/wrangler, @MattieTK, @cloudflare/dev-plat-leads, @vy-ton
/src/content/docs/workers/wrangler/ @cloudflare/wrangler, @irvinebroque, @cloudflare/product-owners, @MattieTK, @vy-ton

@ask-bonk ask-bonk Bot left a comment

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.

Verdict: 2 style issues to address. Inline suggestions posted.

Issues

  • LOW src/content/changelog/workers/2026-06-16-test-harness-api.mdx:2 — API name in changelog title should be wrapped in backticks.
  • LOW src/content/docs/workers/testing/unstable_startworker.mdx:20 — Workers TypeScript examples must use the TypeScriptExample component instead of a bare ts fence.

Out-of-scope note

PR references a workers-sdk fixture that will only exist once cloudflare/workers-sdk#14278 is merged. Coordinate merge timing so the link does not 404.

Comment thread src/content/changelog/workers/2026-06-16-test-harness-api.mdx Outdated
Comment thread src/content/docs/workers/testing/unstable_startworker.mdx
@ask-bonk

ask-bonk Bot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Review posted on PR #31422.

Labels: Already appropriately labeled (product:workers, product:changelog, size/l). No changes needed.

What this PR changes

  • Adds a new Test harness documentation section (/workers/testing/test-harness/) with an overview, a "Write your first test" guide, and an examples page covering multi-Worker routing, outbound request mocking with MSW, Playwright fixtures, and log assertions.
  • Adds a changelog entry for the new createTestHarness() API.
  • Adds comprehensive API reference docs for createTestHarness() in the Wrangler API page.
  • Deprecates unstable_startWorker() and unstable_dev(), pointing users to createTestHarness() for integration tests and the Vite createServer() API for dev server use cases.
  • Updates the Workers testing landing page to distinguish unit tests (Vitest integration) from integration tests (createTestHarness()), replacing the comparison table.

Issues found

  • LOW — Changelog title should monospace the API name: `createTestHarness`.
  • LOW — The updated unstable_startWorker() example uses a bare ts fence instead of the TypeScriptExample component.
  • Note — The examples page links to a workers-sdk fixture that will only exist once cloudflare/workers-sdk#14278 is merged. Coordinate merge timing to avoid a broken link.

Both style issues have inline suggestions on the PR.

github run

@ask-bonk

ask-bonk Bot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Review for PR #31422

Labels: Already appropriately labeled (product:workers, product:changelog, size/l). No changes needed.

What this PR changes

  • Introduces a new Test harness section (/workers/testing/test-harness/) with an overview, a getting-started guide, and an examples page covering multi-Worker routing, outbound request mocking with MSW, Playwright fixtures, log assertions, and debug output.
  • Adds a changelog entry for the createTestHarness() API.
  • Adds comprehensive API reference docs for createTestHarness() to the Wrangler API page, documenting parameters, return types (TestHarness, WorkerHandle), and usage.
  • Deprecates unstable_startWorker() and unstable_dev(), redirecting users to createTestHarness() for integration tests and the Vite createServer() API for programmatic dev server use.
  • Updates the Workers testing landing page and Miniflare/Vitest pages to distinguish unit tests (Vitest integration) from integration tests (createTestHarness()).

Validation status

  • pnpm run check — passed (0 errors)
  • pnpm run lint — passed
  • pnpm run format:core:check — passed

Issues found

Severity File Issue
LOW changelog/workers/2026-06-16-test-harness-api.mdx:2 API name in changelog title should be monospaced.
LOW testing/unstable_startworker.mdx:20 Workers TypeScript examples must use the TypeScriptExample component.
LOW testing/test-harness/examples.mdx:12-16 WranglerConfig is imported but never used.
LOW testing/test-harness/examples.mdx:164 Unused primaryResponse variable in example snippet.
LOW wrangler/api.mdx:101 Placeholder compatibility_date should be a realistic value.
NOTE testing/test-harness/examples.mdx:18 External link to workers-sdk fixture will 404 until cloudflare/workers-sdk#14278 is merged.

Inline suggestions

src/content/changelog/workers/2026-06-16-test-harness-api.mdx

-title: Test Workers the way they are built with createTestHarness
+title: Test Workers the way they are built with `createTestHarness`

src/content/docs/workers/testing/unstable_startworker.mdx
Replace the bare ts fence with TypeScriptExample and add the required import:

+import { TypeScriptExample } from "~/components";

 :::caution
 `unstable_startWorker()` is deprecated. Cloudflare recommends using the [`createTestHarness()`](/workers/testing/test-harness/) API, which provides a harness specifically designed for integration testing.
 :::
 
 The [`unstable_startWorker()`](/workers/wrangler/api/#unstable_startworker) API exposes the internals of the Wrangler dev server, and allows you to customize how it runs. Compared to using [Miniflare directly for testing](/workers/testing/miniflare/writing-tests/), you can pass in a Wrangler configuration file, and it will automatically load the configuration for you.
 
 This example uses `node:test`, but should apply to any testing framework:
 
-```ts
+<TypeScriptExample>
+
+```ts
 import assert from "node:assert";
 import test, { after, before, describe } from "node:test";
 import { unstable_startWorker } from "wrangler";
@@ ... @@
 	after(async () => {
 		await worker.dispose();
 	});
 });
+

+


**`src/content/docs/workers/testing/test-harness/examples.mdx`**
Remove the unused `WranglerConfig` import:

```suggestion
 import {
 	PackageManagers,
 	TypeScriptExample,
-	WranglerConfig,
 } from "~/components";

Remove the unused primaryResponse variable in the multi-Worker example:

-const primaryResponse = await server.fetch("/");
 const apiResponse = await server.fetch("http://api.example.com/v1/users/123");
 const webResponse = await server.fetch("http://example.com/users/123");

src/content/docs/workers/wrangler/api.mdx
Use a realistic compatibility_date instead of a placeholder:

-				compatibility_date: "YYYY-MM-DD",
+				compatibility_date: "2026-06-12",

Out-of-scope note
The examples page links to a workers-sdk fixture (create-test-harness-example) that will only exist once cloudflare/workers-sdk#14278 is merged. Coordinate merge timing so the link does not 404.

github run

@github-actions

github-actions Bot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Preview URL: https://70635e7f.preview.developers.cloudflare.com
Preview Branch URL: https://edmundhung-test-harness-guide.preview.developers.cloudflare.com

Files with changes (up to 15)

Original Link Updated Link
https://developers.cloudflare.com/workers/testing/test-harness/examples/ https://edmundhung-test-harness-guide.preview.developers.cloudflare.com/workers/testing/test-harness/examples/
https://developers.cloudflare.com/workers/wrangler/api/ https://edmundhung-test-harness-guide.preview.developers.cloudflare.com/workers/wrangler/api/
https://developers.cloudflare.com/changelog/post/2026-06-18-test-harness-api/ https://edmundhung-test-harness-guide.preview.developers.cloudflare.com/changelog/post/2026-06-18-test-harness-api/
https://developers.cloudflare.com/workers/testing/unstable_startworker/ https://edmundhung-test-harness-guide.preview.developers.cloudflare.com/workers/testing/unstable_startworker/
https://developers.cloudflare.com/workers/testing/test-harness/write-your-first-test/ https://edmundhung-test-harness-guide.preview.developers.cloudflare.com/workers/testing/test-harness/write-your-first-test/
https://developers.cloudflare.com/workers/testing/ https://edmundhung-test-harness-guide.preview.developers.cloudflare.com/workers/testing/
https://developers.cloudflare.com/workers/testing/test-harness/ https://edmundhung-test-harness-guide.preview.developers.cloudflare.com/workers/testing/test-harness/
https://developers.cloudflare.com/workers/testing/vitest-integration/migration-guides/migrate-from-unstable-dev/ https://edmundhung-test-harness-guide.preview.developers.cloudflare.com/workers/testing/vitest-integration/migration-guides/migrate-from-unstable-dev/
https://developers.cloudflare.com/workers/testing/miniflare/ https://edmundhung-test-harness-guide.preview.developers.cloudflare.com/workers/testing/miniflare/
https://developers.cloudflare.com/workers/testing/miniflare/writing-tests/ https://edmundhung-test-harness-guide.preview.developers.cloudflare.com/workers/testing/miniflare/writing-tests/
https://developers.cloudflare.com/workers/testing/vitest-integration/ https://edmundhung-test-harness-guide.preview.developers.cloudflare.com/workers/testing/vitest-integration/

@cloudflare cloudflare deleted a comment from cloudflare-docs-bot Bot Jun 13, 2026
@cloudflare cloudflare deleted a comment from cloudflare-docs-bot Bot Jun 13, 2026
mvvmm added a commit that referenced this pull request Jun 13, 2026
…ams polling

Previously each style-guide child dispatch used ?wait=result, holding a
synchronous HTTP request open to the child DO until it completed. If that
response path dropped (child finished but parent never received the result),
the parent's Promise.all would hang indefinitely. Observed in PR #31422:
all 11 child runs completed on the child side, but one parent-side
style_guide_child_dispatch_complete was never logged.

New approach:
- Invoke each child in accepted mode (no ?wait=result); receive runId in 202
- Poll /runs/:runId via Durable Streams long-poll until run_end is observed
- Each long-poll subrequest has a bounded lifetime (<=30s per Flue protocol)
- Resume from offset after each response so no events are missed
- If run_end.isError, throw with the actual error message
- If deadline (5 min) exceeded without run_end, return empty result with
  a timeout log rather than hanging the parent forever

New log actions:
- style_guide_child_run_admitted
- style_guide_child_run_observe_start
- style_guide_child_run_observe_complete
- style_guide_child_run_observe_failed
- style_guide_child_run_observe_timeout
- style_guide_child_run_observe_fetch_error
- style_guide_child_run_observe_error
@@ -0,0 +1,81 @@
---
title: Test Workers the way they are built with createTestHarness
description: Run integration tests against your Workers' production build output from any Node.js test runner.

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 line is much clearer than "the way they are built" in the title

Run tests against your Worker's production build using any Node.js test runner

Something like that?

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.

I would prefer keeping integration tests in the title to distinguish this from vitest-pool-workers. How about this?

title: Run integration tests against your Worker's production build
description: Use Wrangler's `createTestHarness` API to test production build output from any Node.js test runner.

Comment thread src/content/changelog/workers/2026-06-16-test-harness-api.mdx Outdated
Comment thread src/content/changelog/workers/2026-06-16-test-harness-api.mdx Outdated
});

test("routes requests to each Worker", async ({ expect }) => {
network.use(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Is this right? Doesn't seem to relate to the rest of the test in terms of routing @edmundhung

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.

It's there to showcase how to mock outbound request. We can drop it.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

If we're going to keep it I'd just put a comment line above showing that, it's not clear how it relates to this example.

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.

Added a comment above in 460a32a. I have also moved the publish date to tomorrow (18 Jun).

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.

Just updated in 70635e7 to mention both two unstable APIs are now deprecated too.

@cloudflare-docs-bot

cloudflare-docs-bot Bot commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Review

⏸️ Automatic reviews for this PR are paused.

This PR has already received 2 automatic reviews. To run another review, a codeowner can comment /review or /full-review.

Tip: Keep PRs in draft mode until they are ready for review — the bot skips draft PRs automatically.


⚠️ 1 warning and 1 suggestion found in commit ae91833.

Warnings (1)
File Issue
workers/testing/vitest-integration/index.mdx line 13 Avoid deprecated jargon: out-of-the-box — Line contains prose phrase "out-of-the-box support for TypeScript" Fix: Replace with "default support for TypeScript" or similar wording without the deprecated jargon.
Suggestions (1)
File Issue
workers/testing/vitest-integration/migration-guides/migrate-from-unstable-dev.mdx line 18 Passive voice — Added line uses passive voice: "The unstable_dev API has been a recommended approach to run integration tests." Fix: Rewrite in active voice, for example: "Cloudflare previously recommended the unstable_dev API for integration tests."
Commands

Only codeowners can run commands. Post a comment with the command to trigger it.

Command Description
/review Runs a review now. Incremental if a prior review exists, full if not.
/full-review Re-reviews the entire PR diff from scratch, ignoring incremental history. Useful after a rebase, when you want a fresh review, or if the bot gets out of sync and reports issues that no longer exist.

mvvmm added a commit that referenced this pull request Jun 15, 2026
…ble Streams polling, and refactor domain logic into lib/ (#31436)

* [flue] Reduce style-guide fan-out concurrency and scope child hydration by filename

- Lower STYLE_GUIDE_CONCURRENCY from 10 to 5 to reduce concurrent DO memory pressure
- When a filename is specified, hydrate only manifest.json, pr.json, comments, and
  the single patch file instead of listing every object in the diffDir prefix
- Re-enable and improve fan-out structured logs in code-review-orchestrator:
  style_guide_fanout_start, style_guide_complete, and per-child dispatch events
- Add filename to all style-guide-review log events for per-child observability
- Add hydration_start/hydration_complete logs with object counts

* [flue] Avoid redundant R2 GETs for manifest.json and pr.json in targeted hydration

manifest.json and pr.json are already fetched earlier in the run function
(for the fast-fail check and PR metadata read). Reuse their in-memory text
instead of re-fetching from R2 inside the targeted diffKeysToLoad branch.
Only comments and the patch file are fetched fresh in targeted mode.

* style: format

* [flue] Fix Body already used error in targeted hydration

manifestObj.text() was called at parse time (step 1) and then again
inside the cachedDiffResults block. Same for prObj.json() then prObj.text().
R2 response bodies are single-use streams — reading twice throws TypeError.

Fix: capture manifestText and prText as strings on first read, reuse them
for both parsing and workspace write.

* [flue] Log actual error before Flue swallows it as generic 500

Wrap the style-guide-review run body in a top-level try/catch that
logs the error message and stack with full context (PR number, filename,
diffDir, runId) before rethrowing. Previously any unhandled throw was
caught by Flue and surfaced to the parent orchestrator only as a generic
500 internal_error with no child-side log, making the root cause invisible
in Workers Observability.

* style: format

* [flue] Replace ?wait=result fan-out with accepted mode + Durable Streams polling

Previously each style-guide child dispatch used ?wait=result, holding a
synchronous HTTP request open to the child DO until it completed. If that
response path dropped (child finished but parent never received the result),
the parent's Promise.all would hang indefinitely. Observed in PR #31422:
all 11 child runs completed on the child side, but one parent-side
style_guide_child_dispatch_complete was never logged.

New approach:
- Invoke each child in accepted mode (no ?wait=result); receive runId in 202
- Poll /runs/:runId via Durable Streams long-poll until run_end is observed
- Each long-poll subrequest has a bounded lifetime (<=30s per Flue protocol)
- Resume from offset after each response so no events are missed
- If run_end.isError, throw with the actual error message
- If deadline (5 min) exceeded without run_end, return empty result with
  a timeout log rather than hanging the parent forever

New log actions:
- style_guide_child_run_admitted
- style_guide_child_run_observe_start
- style_guide_child_run_observe_complete
- style_guide_child_run_observe_failed
- style_guide_child_run_observe_timeout
- style_guide_child_run_observe_fetch_error
- style_guide_child_run_observe_error

* style: format

* [flue] Apply accepted-mode + Durable Streams polling pattern everywhere

Extract shared admitWorkflow() and pollRun() helpers into lib/poll-run.ts
so the pattern is consistent across all orchestrators.

orchestrate.ts:
- Dependabot PR events (3a): fire-and-forget admitted mode — result is not
  needed by the orchestrate workflow; the dependabot-review workflow handles
  its own GitHub output
- /full-review and /review slash commands (3, 4): same — fire-and-forget;
  result discarded in original code too
- Spam filter (5): admit + poll, since the closed boolean determines whether
  to run code review. Timeout treated as 'not spam' to avoid blocking review
- Code review orchestrator (6): fire-and-forget admitted mode — it posts its
  own GitHub comment when done; orchestrate does not need to wait

code-review-orchestrator.ts:
- Replace inline dispatchStyleGuideReview / pollStyleGuideRun with a thin
  wrapper over the shared admitWorkflow() + pollRun() helpers
- Keeps all existing structured log actions intact

lib/poll-run.ts (new):
- admitWorkflow(): POST to any workflow pathname, return runId from 202
- pollRun<T>(): poll /runs/:runId via Durable Streams long-poll until
  run_end, with configurable timeout and transient error retry

* style: format

* [flue] Upgrade model to kimi-k2.7-code across all workflows

* [flue] Extract model constants into lib/models.ts

Add lib/models.ts with PRIMARY_MODEL and RECONCILIATION_MODEL.
Replace hardcoded model strings in all four workflow files.

* Revert "[flue] Extract model constants into lib/models.ts"

This reverts commit 23ab445.

* [flue] Extract style-guide result types/schemas into lib/style-guide-results.ts

Move StyleGuideFindingFromModelSchema, StyleGuideResultFromModelSchema,
StyleGuideFinding, StyleGuideResult, and assignFindingIds out of the
workflow file into a shared lib. style-guide-review.ts re-exports the
public types; code-review-orchestrator.ts imports directly from lib.

* style: format

* [flue] Extract style-guide workspace hydration into lib/style-guide-hydration.ts

* style: format

* [flue] Extract style-guide fan-out mechanics into lib/style-guide-fanout.ts

* style: format

* [flue] Extract code-review diff/state helpers into lib/code-review-diff.ts and lib/code-review-state.ts

* style: format

* [flue] Extract code-review comment rendering into lib/code-review-render.ts

* style: format

* [flue] Extract GitHub webhook parsing helpers into lib/github-webhook.ts

* style: format

* [flue] Extract spam-filter domain helpers into lib/spam-filter.ts

* style: format

* [flue] Extract Dependabot review helpers into lib/dependabot-review.ts

* style: format

* [flue] Fix redundant R2 GETs in full hydration mode and misleading acted:true on dispatch failure

---------

Co-authored-by: cloudflare-docs-bot[bot] <cloudflare-docs-bot[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants