![]() |
VOOZH | about |
dotnet add package Thunder.DocSnippets --version 0.1.3
NuGet\Install-Package Thunder.DocSnippets -Version 0.1.3
<PackageReference Include="Thunder.DocSnippets" Version="0.1.3" />
<PackageVersion Include="Thunder.DocSnippets" Version="0.1.3" />Directory.Packages.props
<PackageReference Include="Thunder.DocSnippets" />Project file
paket add Thunder.DocSnippets --version 0.1.3
#r "nuget: Thunder.DocSnippets, 0.1.3"
#:package Thunder.DocSnippets@0.1.3
#addin nuget:?package=Thunder.DocSnippets&version=0.1.3Install as a Cake Addin
#tool nuget:?package=Thunder.DocSnippets&version=0.1.3Install as a Cake Tool
Make your C# XML doc <code> examples runnable as unit tests — the Rust doctest experience for .NET.
DocSnippets is a Roslyn source generator. Add the package, and every <code> block in your XML documentation becomes a compiled, executable NUnit/xUnit/MSTest test method. No extra tooling, no checked-in generated files.
<PackageReference Include="Thunder.DocSnippets" Version="..." />
Add DocSnippets to your test project:
<PackageReference Include="Thunder.DocSnippets" Version="..." />
Run dotnet test. Done.
DocSnippets ships an MSBuild .targets file that automatically wires up the adjacent source project's .cs files as AdditionalFiles — no manual <ItemGroup> configuration required.
Any <code> block in an XML doc comment is picked up automatically:
/// <summary>Adds two integers.</summary>
/// <example>
/// <code>
/// var result = calculator.Add(1, 2); // => 3
/// </code>
/// </example>
public int Add(int a, int b) => a + b;
The assertion comment is transformed into a framework-appropriate equality check at generation time. No assertion library is required in the snippet itself.
All four comment styles work on both declaration lines and expression lines:
| Pattern | Example | Meaning |
|---|---|---|
// => VALUE |
var x = f(); // => 42 |
Assert x equals 42 |
// result: VALUE |
var x = f(); // result: 42 |
Assert x equals 42 |
// VARNAME: VALUE |
DoSetup(); // myVar: 42 |
Assert myVar equals 42 |
// VALUE (bare literal) |
var x = f(); // 42 |
Assert x equals 42 |
On a declaration line (var x = f(); // => 3), the generator splits the line into the declaration statement and a separate assertion using the declared variable name. On an expression line (f(); // => 3), the entire expression becomes the assertion subject.
Bare literal comments (// 42, // true, // "hello") are only treated as assertions when the comment contains an unambiguous C# literal. Non-literal comments (// sum of values, // see above) are left unchanged.
Add // doctest-ignore on the first line of any <code> block to skip it:
/// <code>
/// // doctest-ignore
/// // This example is illustrative — not executable.
/// var x = SomeExternalCall();
/// </code>
Some examples are valid documentation but not self-contained — they reference live objects (GraphicsDevice, SpriteBatch, etc.) that can't be instantiated in a test without boilerplate that would pollute the rendered docs. Mark these with // doctest-compile-only:
/// <code>
/// // doctest-compile-only
/// spriteBatch.Begin();
/// spriteBatch.Draw(texture, position, Color.White);
/// spriteBatch.End();
/// </code>
The generated test is compiled (so rename refactors and API breakages are caught) but skipped at runtime. DocSnippets automatically stubs any undeclared identifiers (spriteBatch, texture, position) by looking them up in the project's symbol table — no manual stub declarations needed. The skip attribute varies by framework:
| Framework | Attribute |
|---|---|
| NUnit | [Test, Ignore("compile-only")] |
| xUnit | [Fact(Skip = "compile-only")] |
| MSTest | [TestMethod, Ignore] |
Assertion comments (// => VALUE) are not transformed in compile-only snippets — they are emitted verbatim as comments.
docsnippets.json)Add a docsnippets.json file alongside the source files (it is picked up automatically as an AdditionalFile by the shipped MSBuild targets) to configure the generator:
{
"snippetMode": "opt-out",
"assertionStyle": "NUnit",
"implicitUsings": ["MyLib", "MyLib.Models"]
}
| Key | Values | Default | Description |
|---|---|---|---|
snippetMode |
"opt-out", "opt-in" |
"opt-out" |
opt-out: all <code> blocks run. opt-in: only blocks containing // doctest run. |
assertionStyle |
"NUnit", "XUnit", "MSTest" |
(auto-detect) | Override the auto-detected test framework. |
implicitUsings |
Array of namespace strings | [] |
Namespaces added as using to every generated test class. |
Mark individual snippets with // doctest to include them when snippetMode is "opt-in":
/// <code>
/// // doctest
/// var x = Multiply(3, 4); // => 12
/// </code>
InternalsVisibleToIf your snippets reference internal types, add this to your source project:
[assembly: InternalsVisibleTo("YourTestProject")]
Without it, snippets referencing internal members produce CS0122 errors that can be hard to diagnose.
var (a, b) = f(); // => ... is not supported. Use a separate assertion line.await are not supported. The emitted test method is synchronous; await will produce a compile error.ProjectReference source projects are scanned. Transitive references are not.// doctest-compile-only — compile-checked-but-skipped snippets with automatic stub inference for undeclared identifiers// => ~3.14 (one decimal place tolerance), // => ~3.14 (2dp) for explicit precision// => VALUE, // result: VALUE, // VARNAME: VALUE, bare literal // 42#line diagnostics — build errors in generated test methods now point to the original source line, not the generated fileassertionStyle config override — override auto-detected framework via docsnippets.json//=> arrow alias — //=> accepted as shorthand for // =>Add_ShouldBe_3 naming from // => assertionsoperator +, ~MyClass, etc.snippetMode: opt-in — only run explicitly marked snippetsAdditionalFiles glob injected via shipped .targets fileLearn more about Target Frameworks and .NET Standard.
This package is not used by any NuGet packages.
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.1.3 | 101 | 4/30/2026 |
| 0.1.2 | 91 | 4/30/2026 |
| 0.1.1 | 108 | 4/28/2026 |
| 0.1.1-preview9 | 112 | 4/27/2026 |
| 0.1.1-preview8 | 100 | 4/27/2026 |
| 0.1.0 | 101 | 4/26/2026 |
| 0.1.0-preview7.1 | 58 | 4/26/2026 |
| 0.1.0-preview7 | 95 | 4/26/2026 |
| 0.1.0-preview6 | 92 | 4/25/2026 |