Back to Podcast Digest
dotnet1h 1m

Blazor Community Standup: E2E Component Testing for Blazor

TL;DR

  • Microsoft is building a new end-to-end component testing library for Blazor in .NET 11 previews — Javier Calvarro Nelson calls it “the web application factory of end-to-end testing,” designed to launch your app as a real process and drive it through Playwright instead of relying on in-memory HTML parsing.

  • The big goal is reliability, not just convenience — the team is baking in patterns they learned from running 1,000+ Blazor E2E tests internally, including ways to test pre-rendering, streaming rendering, enhanced navigation, and app startup failures without flaky timing hacks.

  • They solved the classic port-collision problem with YARP and header-based routing — each app instance runs on a random free port, while Playwright talks to a stable launch-settings URL proxied through YARP, which routes requests to the right test instance using a special header.

  • Service overrides still work, but via a pretty clever startup-hook mechanism — a source generator finds static test override methods, then the test assembly gets injected into the launched app process so those methods can modify DI during startup, similar in spirit to WebApplicationFactory.

  • Observability is treated as a first-class feature because both humans and AI need it — traces, videos, retries, and artifact capture are intended to happen by default so a failing test leaves behind enough evidence for a developer or agent to diagnose what went wrong.

  • This will ship experimentally first, likely usable in earnest around .NET 11 Preview 5 — Preview 4 may contain the NuGet package with rough edges, but Daniel Roth and Javier were explicit that Playwright+xUnit is the current path and framework support is still an open question.

The Breakdown

Why WebApplicationFactory Wasn’t Enough

Daniel Roth opens by framing this as the next step after .NET 10’s improvements to end-to-end testing. WebApplicationFactory was originally built about 10 years ago for MVC and APIs, Javier says, and while it can be stretched to launch Kestrel for browser tests, that was always more of a stopgap than a true E2E solution.

The Team Is Fixing Its Own Testing Pain

A recurring theme is that ASP.NET Core tends to productize what the team needs itself. Javier says Blazor’s internal E2E infrastructure grew “organically over the years” and isn’t great, but now agentic development makes the cleanup practical — work that once might have taken one or two months of manual test rewrites can now be done much faster once the pattern is set.

Reliable Blazor Testing Means Controlling Time and State

The hard part isn’t launching a browser; it’s testing Blazor-specific behaviors without flakes. Javier walks through the kinds of scenarios they care about: stopping the app before interactivity kicks in, asserting pre-rendered output, then releasing it and checking the interactive UI; or validating streaming rendering where content appears and gets replaced over time.

AI Is Part of the Design Brief

Javier is unusually direct that this library is also being built for an AI-heavy workflow. If agents are writing tests and iterating on features, then observability becomes essential, so the plan is to capture traces, videos, retries, and artifacts by default so failures are legible to both a human and an LLM.

How the New Library Actually Starts and Routes Apps

The prototype, now renamed in the repo, launches apps as real processes via dotnet run or against published output for production-like testing — especially important for Blazor WebAssembly trimming and publishing behavior. To avoid port conflicts while still giving tests a stable URL, the library starts each app on a random port and puts a local YARP proxy in front, with Playwright adding a header so requests get routed to the correct instance.

Today It’s Playwright and xUnit — Intentionally

Daniel asks the obvious question: is this tied to Playwright? Javier says yes, for now it is tied to Playwright and xUnit, and that’s one reason it will stay preview/experimental for a while; they may eventually split out a core layer, but they’re also wary of supporting every framework under the sun instead of picking the setup that best fits the experience they want.

The Clever Bit: DI Overrides Inside a Real Process

One of the most interesting moments is Javier’s explanation of how service overrides survive in a true browser-based test setup. A source generator finds ConfigureServices-style static methods, the test assembly is injected into the launched app process via a .NET startup hook, and a hosting startup mechanism applies those service changes during app startup — basically recreating one of WebApplicationFactory’s best features, but in a real running app.

Testing Pre-Rendering, Loading States, and the “Distributed TaskCompletionSource” Trick

For flaky UI phases, the team is building first-class primitives instead of leaving developers to invent timing hacks. Javier shows examples like intercepting blazor.web.js to freeze the app before interactivity, and a lock mechanism he jokingly describes like a “distributed task completion source” so tests can hold a weather service response, assert the loading state, then release the lock and verify the final table render.

What It Is — and Isn’t — for Aspire

Toward the end, they clarify the scope: this is more about end-to-end testing of the UI frontend than spinning up an entire distributed system. Aspire may eventually complement this story, especially for orchestrating dependencies, but Javier’s advice is pragmatic — many teams should still run the UI and mock backend services, because the bigger the system, the higher the cost of truly full-stack end-to-end testing.

When You Can Try It

The package should begin appearing in the .NET 11 release train, with Daniel saying Preview 4 may contain early bits but Preview 5 is the more realistic point for people to start trying it seriously. They repeatedly stress that this is a work in progress and want feedback in the ASP.NET Core repo before deciding what the final shape should be.

Share