C# guide

From .NET test to browser AppBundle.

Build on the dependency-free .NET 8 lifecycle, keep ordinary MSTest as the native source of diagnostics, and publish the same parameterless test methods through a focused .NET 10 WebAssembly host.

1. Choose the lane

WorkRequired runtimePackage
Use VanillaTestSuite natively.NET 8 or newervanilla-test 2.1.0
Run ordinary MSTest nativelyYour supported .NET test SDKMSTest adapter/framework
Publish browser WebAssembly.NET 10 SDK + wasm-toolsvanilla-test.tool 2.1.0

The core library does not require the browser tool. Add the tool only to projects that need a deployable browser test.

2. Install the core

gh release download 2.1.3 --repo RIAEvangelist/vanilla-test --pattern '*.nupkg' --dir ./packages
dotnet add package vanilla-test --version 2.1.0 --source ./packages

The package is currently a GitHub release asset rather than a NuGet.org package. Verify its SHA-256 digest, then use the download directory as a local source. The core targets net8.0 and has no third-party runtime dependencies.

using VanillaTest;

var test = new VanillaTestSuite();
test.Expects("addition preserves the total");
if (1 + 2 == 3) test.Pass(); else test.Fail();
test.Done();

TestResult result = test.Report();
if (!result.Ok) throw new InvalidOperationException(result.Report);

Call Done() for every active case. Calling it without a prior decision intentionally records a failure. Once Report() succeeds, the same result object is returned on every later call.

3. Keep the inventory in MSTest

A browser-capable test project can use the ordinary MSTest attributes. This minimal project pins the versions used by this repository:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net10.0</TargetFramework>
    <IsTestProject>true</IsTestProject>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.9.0" />
    <PackageReference Include="MSTest.TestAdapter" Version="4.3.3" />
    <PackageReference Include="MSTest.TestFramework" Version="4.3.3" />
    <PackageReference Include="vanilla-test" Version="2.1.0" />
  </ItemGroup>
</Project>

Run it natively first:

dotnet test --configuration Release

Native MSTest is the canonical path for filters, data-driven cases, runner integrations, and complete failure diagnostics.

4. Build the browser AppBundle

dotnet workload install wasm-tools
dotnet tool install --global vanilla-test.tool --version 2.1.0 --add-source ./packages
dotnet vanilla-test --browser --project tests/MyProject.Tests.csproj

The tool resolves paths from the selected project, creates a generated host under obj/vanilla-test/browser-host/, and publishes a Release net10.0 application for browser-wasm. With no --out-dir, the AppBundle lands in dist/vanilla-test/ beside that project.

dist/vanilla-test/
├── index.html
├── browser.js
├── runtests.mjs
├── _framework/
└── .vanilla-test-browser-output

This is a multi-file deployment. Keep the complete _framework/ tree beside the page and scripts. The ownership marker lets later builds safely refresh the directory; the tool refuses to overwrite an unrelated nonempty destination.

Stable host, narrow contract.

MSTest 4.3.3 does not expose the stable Microsoft.Testing.Platform browser path this lane needs. The generated host therefore uses MSTest attributes plus deterministic reflection. It runs public, parameterless test methods; parameterized and data-expanded runner features stay native.

5. Test a custom page

Start from the tool's built-in runner, preserve its required hooks, and add visible site checks:

<h1
  data-csharp-browser-site-check="browser site renders its heading"
  data-csharp-browser-expected="Dashboard tests"
>Dashboard tests</h1>

<code data-csharp-browser-target>browser-wasm</code>
<p data-csharp-browser-result>Starting…</p>
<strong data-csharp-browser-score>0/?</strong>
<ol data-csharp-browser-checks></ol>
<pre data-csharp-browser-console></pre>
<script type="module" src="./browser.js"></script>
dotnet vanilla-test --browser \
  --project tests/MyProject.Tests.csproj \
  --page tests/browser.html \
  --out-dir artifacts/browser

The custom page becomes the AppBundle's index.html. Each marked element must be visible. When data-csharp-browser-expected is present, its rendered text must match exactly. The target marker must say browser-wasm.

6. Serve and verify the whole directory

npx --yes node-http-server@10.0.0 \
  --root dist/vanilla-test \
  --port 4173

Open http://localhost:4173/. The pinned node-http-server command serves the complete AppBundle from its generated root. The page imports _framework/dotnet.js, captures the .NET console stream, renders browser checks, and verifies that the MSTest summary has no failed methods. Static hosting must preserve the generated paths and serve WebAssembly assets with an appropriate MIME type.

For a command-line CI pass, run the included Node entry from the AppBundle:

node tests/dist/vanilla-test/runtests.mjs --output Detailed

7. Know what the reflection host runs

  • Public instance methods with [TestMethod] on [TestClass] types are discovered in deterministic type-and-method order.
  • A fresh class instance is constructed for every method.
  • Parameterless synchronous, Task, and ValueTask methods are supported.
  • [Ignore], [TestInitialize], [TestCleanup], IDisposable, and IAsyncDisposable are honored.
  • Parameterized methods fail with an explicit unsupported message; data-row expansion and TestContext injection are not emulated.

The repository uses this exact host to run its same seven core MSTest methods, including the Behavioral Given/When/Then journey, in the browser. Use native dotnet test when a suite depends on broader adapter behavior.

Next

Copy

Complete examples

Use the native class, MSTest inventory, CLI invocation, or custom page directly.

Open C# examples →
Inspect

API contract

Review every public member, stable error code, result property, and tool option.

Open the C# API →