dotnet / dotnet/aspnetcore

Blazor Validation Testing Manual

Open
#68,479 0 comments 0 reactions 1 assignee Claimed by @oroztocil View on GitHub
area-blazor Validation
Dominant language
C#
Stars
38.4k
Forks
10.9k
Avg merge
2d 6h
Merged PRs (30d)
290

Description

The following document is intended as a guide for testers working on validation testing for new Blazor features.

## What we are testing

Testing scenarios are published as issues in the `dotnet/aspnetcore` repository labeled with the [`validation-scenario` label](https://github.com/dotnet/aspnetcore/issues?q=is%3Aissue%20state%3Aopen%20label%3Avalidation-scenario).

For each scenario, you are asked to build a small, real application, using the same packages, templates, and tooling a customer would use. The goal is comprehensive testing that covers multiple angles:

1. **Correctness.** Does the feature behave as described, in every configuration the scenario lists, on the build that will ship?
2. **Developer experience.** Was the feature discoverable and easy to use? Were the APIs, defaults, and error messages clear?
3. **Documentation.** Could you accomplish the task using the published documentation alone, and was it accurate and complete?

While testing a feature, **do not study its implementation** in the repository. Use links provided in the scenario description, or search for public user-facing information such as release notes or MSDN pages. Read the XML documentation for the tested APIs surfaced by the IDE (mouse over, F12).

## Before you start

### Install and verify the build

Scenarios are validated against the current .NET 11 preview or release candidate. Which of the two routes below you need depends on whether the build you want has shipped yet. Prefer a released build whenever the scenario's minimum build has shipped, and use a daily build only when it has not.

**A released preview.** Download the SDK from the [.NET 11 downloads page](https://dotnet.microsoft.com/download/dotnet/11.0). Every release is listed in the [.NET 11 release notes](https://github.com/dotnet/core/blob/main/release-notes/11.0/README.md), which is the quickest way to confirm the exact version string.

**A build that has not shipped yet.** Take the daily build for the release branch from the [.NET SDK builds table](https://github.com/dotnet/dotnet/blob/main/docs/builds-table.md), which carries one column per active branch. For example, for the .NET 11 Preview 7 branch on Windows x64 that is [dotnet-sdk-win-x64.exe](https://aka.ms/dotnet/11.0.1xx-preview7/daily/dotnet-sdk-win-x64.exe), and the matching [productCommit-win-x64.txt](https://aka.ms/dotnet/11.0.1xx-preview7/daily/productCommit-win-x64.txt) records the exact commits it was built from. Daily builds change without notice, so record that commit next to the version number and include it in your report.

Verify what versions you actually have:

```bash
dotnet --info
dotnet --list-sdks
dotnet --list-runtimes
```

Pin the SDK so that another installation cannot silently take over:

```bash
dotnet new globaljson --sdk-version --roll-forward latestPatch
```

Commit that `global.json` with your sample app. It is what makes your result reproducible later.

### Visual Studio, Visual Studio Code, and workloads

Preview SDKs need preview IDE tooling:

- **Visual Studio 2026:** Install the [Preview channel](https://visualstudio.microsoft.com/vs/preview/). Then enable **Tools > Options > Environment > Preview Features > Use previews of the .NET SDK** and restart, otherwise the IDE will quietly fall back to an older SDK.
- **Visual Studio Code:** Install the C# Dev Kit, and switch it to the **pre-release version**.

Some scenarios need workloads on top of the SDK:

* `dotnet workload install wasm-tools`, for publishing a WebAssembly app trimmed or ahead-of-time compiled.
* `dotnet workload install maui`, for the Hybrid scenarios.

### Package feeds

For a released preview, nuget.org is enough. Preview packages are published there, so no `NuGet.config` is needed.

For a daily build, add a `NuGet.config` next to your app:

```xml





```

Keep that file with the app rather than editing the machine-wide configuration. Commit it with the sample app alongside `global.json`.

### Set up somewhere to put evidence

Create a folder for the scenario before you start and drop artifacts into it as you go (screenshots, logs, recordings, etc.).

## Building the app

Work the way a developer would who is meeting this feature for the first time and has a problem to solve.

* Start from the stated template and keep the app small enough that the feature is the thing you are dealing with.
* Reach the feature only through public entry points: packages, templates, public APIs, tooling. Do not reverse-engineer the implementation from the dotnet repository.
* Write friction down while it is happening.

### Using AI assistance

Generally speaking, there are three levels of using AI during validation testing:

1. You write the sample code manually and test the app manually. AI is either not involved, or only provides local help such as code completions.
2. You task an AI agent to write the sample code, then you test the app manually.
3. An AI agent both writes the sample code and tests the app (using browser automation and related tools).

The valuable contribution comes from working at the first and/or second level. The third level can be covered by automated workflows.

Ideally, you would work with the feature both manually and with the help of an AI assistant, and report the experience in both cases. While building with AI, follow these rules:

* **Do not let an AI assistant read the framework source or the implementation pull requests.** Otherwise the assistant can work around issues and limitations of the implementation automatically, decreasing the value of the test.
* **Verify any finding before reporting it.** When an assistant flags an issue, reason about its relevancy and verify it manually.

Example of an opening prompt:

```text
You are helping me build a small sample app to validate a .NET 11 Blazor feature before it ships. Work only from public documentation, the package APIs, and compiler/test output. Do not read the ASP.NET Core source, the implementation pull requests, or issue threads, and do not search for them.

If something appears broken, undocumented, or harder than it should be, stop and tell me instead of working around it.
```

### Configurations

Each scenario lists the configurations it applies to. A ticked box means the configuration is relevant to that scenario, not that it has already been tested.

The commands below assume you have pinned the SDK with `global.json` first, so the app is created by the build under test.

#### Blazor Web App

You can use **one app for all four render modes**.

```bash
dotnet new blazor -n MyScenarioApp -int Auto
```

This registers both `AddInteractiveServerComponents()` and `AddInteractiveWebAssemblyComponents()`, maps both render modes, and creates two projects: `MyScenarioApp` and `MyScenarioApp.Client`.

Add `-ai` (`--all-interactive`) to apply the render mode globally at the root instead of per page. Without it, pages are statically rendered by default and you opt in per component via the `@rendermode` directive:

| Configuration | How to get it |
|---|---|
| Static SSR | No `@rendermode` on the component |
| Interactive Server | `@rendermode InteractiveServer` |
| Interactive WebAssembly | `@rendermode InteractiveWebAssembly`, component must live in the `.Client` project |
| Interactive Auto | `@rendermode InteractiveAuto`, component must live in the `.Client` project |

To set a mode globally instead, put it on `` and `` in `Components/App.razor`.

**In Visual Studio:** File > New > Project > **Blazor Web App**. On the **Additional information** page set:

* **Interactive render mode** — `Auto (Server and WebAssembly)`. `Server` and `WebAssembly` limit you to that one mode, `None` gives static SSR only and no `.Client` project.
* **Interactivity location** — `Per page/component` to opt in per component, or `Global` for the whole app. This is disabled when the render mode is `None` or when authentication is enabled.
* **Include sample pages** — keep these on unless the scenario says otherwise, they give you something to click through.

#### Standalone WebAssembly

A separate app. It has no render modes, everything runs in the browser, and it cannot do static SSR.

```bash
dotnet new blazorwasm -n MyScenarioWasm
```

Add `-p` (`--pwa`) if the scenario involves installation or offline behavior.

**In Visual Studio:** File > New > Project > **Blazor WebAssembly Standalone App**. The **Additional information** page has no interactivity options, since there are none to choose. Tick **Progressive Web Application** if the scenario needs it.

#### Hybrid (MAUI)

A separate app. It always renders interactively in a WebView, and `@rendermode` annotations have no effect there.

```bash
dotnet workload install maui
dotnet new maui-blazor -n MyScenarioHybrid
```

Use `maui-blazor-web` instead when the scenario asks you to share components between a Hybrid app and a Blazor Web App. It creates both plus a shared library:

```bash
dotnet new maui-blazor-web -n MyScenarioShared -int Auto
```

**In Visual Studio:** you need the **.NET MAUI** workload installed through the Visual Studio Installer. Then File > New > Project > **.NET MAUI Blazor Hybrid App**, or **.NET MAUI Blazor Hybrid and Web App** for the shared-UI variant, which asks for the web app's interactive render mode.

Building for a device or emulator needs the platform SDKs, so allow extra time the first time you run this.

#### Interactive Auto is two runs

Interactive Auto runs on Server for a first visit and on WebAssembly once resources are cached, so it is **two different runs of the same page**. Test a fresh visit with browser storage cleared, then reload once the app is cached, and report both. Note that the modes are switched after the reload, not automatically during one session (a common misconception).

### Beyond the first run

Scenarios build and run in **Debug** unless the scenario ticks **Published output**, in which case also run the published Release build and report the two separately when they differ.

The items below are independent options, not a sequence. Each scenario's **Also exercise** checklist says which of them apply to it, so work from that rather than through this list.

* **Publish and run the published output.** Several .NET 11 issues appeared only in published applications.

```bash
dotnet publish -c Release
```

* **Upgrade an existing app.** Where the scenario says so, start from an application built on the previous version and move it forward, rather than starting from a new template. Upgrades surface a different class of problem than new projects: new warnings on existing code, behavior that changes silently, and APIs that are gone.
* **Trimming and ahead-of-time compilation.** WebAssembly applications are published trimmed, and anything relying on reflection, resources, or serialization can behave differently there than during development.
* **A proxy or a sub-path in front.** Anything touching URLs, assets, or redirects behaves differently once the app is not at the root of a host. Where the scenario mentions more than one server instance, run two and route between them without sticky sessions.
* **Hot Reload.** Run the app from source with Hot Reload enabled in an IDE, or with `dotnet watch` from the console. Change something and save, then see if this triggers hot reload and if the changes are properly reflected in the running app. This applies to a Debug run from source, not to published output.
* **An IDE as well as the command line.** Do the same steps in Visual Studio or VS Code and at the command line, and report where they disagree: different diagnostics, different build output, or a feature that only surfaces in one of them.
* **Container.** Run the app in a container, either through the template's container support or the IDE's container tooling.

If you skip one that the scenario asked for, say so in the report rather than leaving it ambiguous.

## What to watch for

Check all of these as you work, and capture anything unexpected, unhelpful, or noisy, including things that turn out to be harmless:

* Build errors and warnings, including analyzer diagnostics, their identifiers, and their suggested code fixes.
* Runtime exceptions, and whether the message says what to do rather than only what failed.
* .NET console output, including logs that are too noisy as well as ones that are missing.
* Browser console output and errors.
* Browser network activity, when the scenario involves navigation, assets, downloads, or reconnection.
* The visual result and layout, including what happens in the moments while the page is still loading.
* Accessibility where the feature renders UI: keyboard reachability, focus behavior, and whether labels, errors, and status changes are announced.
* Time and resource use over a longer session, where the scenario involves repeated actions.

Take a screenshot of anything that looks wrong.

## Evidence

Match the evidence to the kind of claim. Capture it for passes too, not only failures: a pass with nothing behind it cannot be told apart later from a scenario nobody ran.

| What you are claiming | What to capture |
|---|---|
| An HTTP or wire behavior | The request and the full response, headers included. `curl -i` output pasted as text. |
| Rendered output | A screenshot, plus the relevant markup copied from developer tools. |
| A build or analyzer behavior | The full build output showing the diagnostic identifier and the line. |
| A runtime failure | The complete exception and stack trace, the .NET console output, and the browser console output. |
| Something about timing, flicker, or a transition | A short screen recording. |
| Memory or resource growth | Before and after measurements, and how long the session ran. |
| Any finding at all | The exact steps to reproduce, starting from the `dotnet new` command. |

## Publishing your sample app

Publish the app as an ordinary GitHub repository under your own account or your organization, and put the link in your report. You can use folders inside a shared repository.

* Link the exact place the app lives, so a reviewer lands on the code rather than on a repository root they have to search.
* Include a short README with the scenario title, the build you tested, and how to run each configuration you covered.
* Commit `global.json`, and the `NuGet.config` if you used a daily build, so the app restores against the same bits later.
* Leave the app in the state that shows the finding. If you carried on working afterwards, say so, or link the commit that shows it.

## Reporting

Report using comments on the scenario issue. Use one comment per configuration where results differ between them.

```markdown
### Test report

* **Build tested:** (full SDK version from `dotnet --info`)
* **Configurations tested:**
* **Also exercised:** (which **Also exercise** boxes you covered, or "none")
* **OS, browser, IDE:**
* **Sample app:** (link to the repository or the folder inside it)
* **Outcome:** works / works with issues / blocked

#### Checks

| Check | Result | Evidence |
|---|---|---|
| (each "Must hold" item from the scenario) | pass / fail / not run | (link or attachment) |

#### Problems

1. **(Classification)** Short summary.
- What happened, what you expected instead, and the steps to reproduce.
- Evidence: (attachment or link)
- See section *Classifying problems* below.

#### Not covered

(Checks or steps you did not get to, and why.)
```

### Classifying problems

Label each finding with one of these so results can be grouped later:

* **Product bug.** The framework behaves incorrectly, crashes, or produces the wrong output.
* **Diagnostic quality.** An error, warning, or log was missing, wrong, misleading, or unhelpful.
* **DX friction.** It works, but it was confusing, awkward, verbose, or hard to discover.
* **Documentation gap.** Documentation was missing, incorrect, incomplete, or hard to find.
* **Accessibility.** Something is unreachable, unannounced, or unusable without a mouse.
* **Scenario problem.** The scenario itself is wrong, unclear, or impossible as written.

## When a scenario is done

A scenario counts as **validated** when all of the following hold:

* Every **Must hold** check passed.
* Every ticked configuration was covered, along with anything ticked under **Also exercise**.
* The run was on a build at or after the scenario's **Minimum build**.
* Each result has evidence attached that someone else can open.

**Results do not carry forward between builds.** A pass on an early preview says nothing about the release candidate, because the code changed underneath it. Expect scenarios to be re-run as later builds appear, and always state which build a result belongs to.

Where a scenario was only partly covered, say so under **Not covered** and leave it open. A partial pass reported as a pass is worse than no result, because nobody goes back to it.

Each scenario has its **contact person** named at the top of the description. When you have finished working on the scenario and have posted the report as a comment, **assign the scenario issue to the contact**. The scenario contact is responsible for closing the issue. They can also answer your questions if you have any issues with the scenario.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.