Purview.SourceGeneratorFramework.Testing
1.0.0-prerelease.33
See the version list below for details.
dotnet add package Purview.SourceGeneratorFramework.Testing --version 1.0.0-prerelease.33
NuGet\Install-Package Purview.SourceGeneratorFramework.Testing -Version 1.0.0-prerelease.33
<PackageReference Include="Purview.SourceGeneratorFramework.Testing" Version="1.0.0-prerelease.33" />
<PackageVersion Include="Purview.SourceGeneratorFramework.Testing" Version="1.0.0-prerelease.33" />
<PackageReference Include="Purview.SourceGeneratorFramework.Testing" />
paket add Purview.SourceGeneratorFramework.Testing --version 1.0.0-prerelease.33
#r "nuget: Purview.SourceGeneratorFramework.Testing, 1.0.0-prerelease.33"
#:package Purview.SourceGeneratorFramework.Testing@1.0.0-prerelease.33
#addin nuget:?package=Purview.SourceGeneratorFramework.Testing&version=1.0.0-prerelease.33&prerelease
#tool nuget:?package=Purview.SourceGeneratorFramework.Testing&version=1.0.0-prerelease.33&prerelease
Purview.SourceGeneratorFramework.Testing
Framework-agnostic test runner and assertions for unit testing incremental C# source generators.
Installation
dotnet add package Purview.SourceGeneratorFramework.Testing
What's included
SourceGeneratorTestRunner<TGenerator>— compiles a snippet of C# source, runs the generator, automatically registers an isolated framework logging sink, and returns aDriverRunResultwith generated syntax trees, the output compilation, and captured log entries.SourceGeneratorTestBase<TGenerator>— abstract base class that accepts anITestOutputinstance for framework-specific logging integration.SourceGeneratorTestOptions— options for configuring references, namespaces, analyzer-config values, output kind, and whether to emit the output compilation to an assembly.DriverRunResult— wrapper aroundGeneratorDriverRunResultthat exposes generated trees, the output compilation, emitted assembly, and log entries.DriverRunResultExtensions— assertion helpers such asAssertNoCompilationErrors,AssertNoGenerationExceptions,AssertSingleGeneratedSource,AssertGeneratedSourceContains, and more.ITestOutput/NullTestOutput— abstraction for capturing generator log output during tests.
Usage
Reference the package from a test project and write a test using the runner directly:
<ItemGroup>
<PackageReference Include="Purview.SourceGeneratorFramework.Testing" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" />
</ItemGroup>
using Purview.SourceGeneratorFramework.Testing;
public class MyGeneratorTests
{
[Test]
public async Task GeneratesExpectedSource()
{
var source = """
[MyNamespace.MyAttribute]
public partial class MyClass { }
""";
var runner = new SourceGeneratorTestRunner<MyGenerator>();
var result = await runner.RunAsync(source);
result.AssertNoCompilationErrors();
var generated = result.AssertSingleGeneratedSource();
// Use your test framework's assertions, e.g. with TUnit:
// await Assert.That(generated).Contains("public static partial class MyClass");
}
}
Or derive from SourceGeneratorTestBase<TGenerator> and plug in your own ITestOutput implementation.
Running the generator in the test project
Sometimes the test project's own source uses types produced by the generator—for example, an
integration test may attach a generated marker attribute to a fixture class while also passing the
generator type to SourceGeneratorTestRunner<TGenerator>.
Reference the generator project twice, once in each role:
<ItemGroup>
<ProjectReference
Include="..\..\src\MyGenerator\MyGenerator.csproj"
PrivateAssets="all"
OutputItemType="Analyzer"
ReferenceOutputAssembly="false"
/>
<ProjectReference
Include="..\..\src\MyGenerator\MyGenerator.csproj"
PrivateAssets="all"
ReferenceOutputAssembly="true"
/>
</ItemGroup>
The analyzer reference makes generated declarations available to the test project's compilation.
The normal reference makes the generator's CLR type available to the testing API. These are
separate from the in-memory compilation created by SourceGeneratorTestRunner; source supplied to
the runner is still compiled and generated independently.
The normal reference also exposes the generator's assembly dependencies to every target framework
of the test project. Keep the generator on the oldest compatible Roslyn version—for example,
Roslyn 4.13 when tests target .NET 8, .NET 9, and .NET 10. A generator built against Roslyn 5 and
System.Collections.Immutable 10 will conflict with the framework assemblies supplied by .NET 8
and .NET 9. Use the framework's RegisterEmbeddedAttribute helper when avoiding a newer Roslyn API
such as AddEmbeddedAttributeDefinition.
Options
Configure a test run with SourceGeneratorTestOptions:
var options = new SourceGeneratorTestOptions
{
IncludeDefaultNamespaces = true,
AdditionalNamespaces = ["MyNamespace"],
AdditionalAssemblyTypes = [typeof(SomeExternalType)],
EnableLogging = true,
AnalyzerConfigOptions = { ["MyGenerator_Disable"] = "true" }
};
// Emitting the output to an assembly is opt-in because it is expensive.
var result = await runner.RunAsync(source, options.Compile());
Compile() is an extension method that preserves the concrete options type. A derived options record
that wants a typed default must hide the inherited SourceGeneratorTestOptions.Default with a typed
static, otherwise Default.Compile() returns the base type:
public record MyTestOptions : SourceGeneratorTestOptions
{
public static new MyTestOptions Default => new();
}
// Returns MyTestOptions with CompileToAssembly enabled.
var result = await runner.RunAsync(source, MyTestOptions.Default.Compile());
Analyzer options are preserved under their supplied keys. Keys without the Roslyn
build_property. prefix are additionally exposed as compiler-visible MSBuild properties, so either
MyGenerator_Disable or build_property.MyGenerator_Disable can be used in tests.
See SourceGeneratorFramework.Testing.TUnit for a ready-made TUnit integration.
Querying produced code with CodeQuery
Every result type exposes a CodeQuery so tests can locate syntax nodes in the produced code:
result.Generated() // DriverRunResult: generated trees (generated-first default)
result.Output() // DriverRunResult: whole output compilation
analyzerResult.Code() // AnalyzerTestResult / CodeFixTestResult: input compilation
codeFixResult.FixedCode() // CodeFixTestResult: fixed source
fixAllResult.FixedCode() // CodeFixFixAllResult / RefactorTestResult: changed documents
CodeQuery provides a Get/Has/TryGet family for declarations and members, generic Get<T>/Has<T>,
syntax-tree lookup, and type-aware matching against TypeReference:
var query = result.Generated();
query.GetClass("ServiceCollectionExtensions").HasMethod(query, "Add", TypeReference.Create<int>());
query.HasProperty("Count", TypeReference.Create<int>());
query.GetMethod("DoWork").HasParameters(query, intType, nullableInt, complexType);
query.GetClass("Widget", "Example.Models"); // namespace-scoped lookup
Get throws SyntaxNotFoundException when nothing matches; Has returns bool. See the
source-generator-testing agent skill for the full reference.
Refactoring tests
RefactoringTestRunner<TRefactoring> runs a CodeRefactoringProvider against a test document:
var runner = new RefactoringTestRunner<MyRefactoringProvider>();
var result = await runner.RunAsync(
source,
new RefactorTestOptions
{
NodeSelector = query => query.GetMethod("M"),
EquivalenceKey = MyRefactoringProvider.EquivalenceKey,
});
result.FixedCode().HasMethod("M"); // query the refactored output
The trigger is a Span or a NodeSelector (which runs against a CodeQuery of the input compilation).
Incremental cache testing
SourceGeneratorTestRunner.RunIncrementalAsync runs the generator over a sequence of source sets using a
single shared driver and captures each run's tracked incremental steps, so tests can prove each pipeline
stage caches correctly:
var result = await runner.RunIncrementalAsync([firstSources, secondSources], options);
var reasons = result.Runs[1].Steps["ForAttribute_MyAttribute"]
.SelectMany(step => step.Outputs.Select(output => output.Reason));
RunIncrementalAsync(sources, options, ct) runs the same source set twice (the common "unchanged rerun is
cached" case). Per-run MSBuild-property changes use new IncrementalRunInput(sources, [...]). Reference
cache tests live in the Purview.SourceGeneratorFramework source repository —
SourceGeneratorShared.UnitTests/IncrementalPipelineCacheTests (framework stages) and
SourceGeneratorFramework.ExampleGenerator.UnitTests/ServiceRegistrationCacheTests (an end-to-end
generator) — and should be replicated into your own test project rather than copied from the package.
License
This project is licensed under the MIT license.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- Microsoft.CodeAnalysis.Analyzers (>= 5.9.0)
- Microsoft.CodeAnalysis.CSharp (>= 4.13.0)
- Microsoft.CodeAnalysis.CSharp.Workspaces (>= 4.13.0)
- Purview.SourceGeneratorFramework (>= 1.0.0-prerelease.33)
- System.Collections.Immutable (>= 8.0.0)
-
net10.0
- Microsoft.CodeAnalysis.Analyzers (>= 5.9.0)
- Microsoft.CodeAnalysis.CSharp (>= 4.13.0)
- Microsoft.CodeAnalysis.CSharp.Workspaces (>= 4.13.0)
- Purview.SourceGeneratorFramework (>= 1.0.0-prerelease.33)
-
net8.0
- Microsoft.CodeAnalysis.Analyzers (>= 5.9.0)
- Microsoft.CodeAnalysis.CSharp (>= 4.13.0)
- Microsoft.CodeAnalysis.CSharp.Workspaces (>= 4.13.0)
- Purview.SourceGeneratorFramework (>= 1.0.0-prerelease.33)
-
net9.0
- Microsoft.CodeAnalysis.Analyzers (>= 5.9.0)
- Microsoft.CodeAnalysis.CSharp (>= 4.13.0)
- Microsoft.CodeAnalysis.CSharp.Workspaces (>= 4.13.0)
- Purview.SourceGeneratorFramework (>= 1.0.0-prerelease.33)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on Purview.SourceGeneratorFramework.Testing:
| Package | Downloads |
|---|---|
|
Purview.SourceGeneratorFramework.Testing.TUnit
Purview SourceGeneratorFramework libraries for building and testing incremental C# source generators. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.0-prerelease.35 | 43 | 9/5/2026 |
| 1.0.0-prerelease.34 | 41 | 9/5/2026 |
| 1.0.0-prerelease.33 | 36 | 9/4/2026 |
| 1.0.0-prerelease.32 | 39 | 9/3/2026 |
| 1.0.0-prerelease.31 | 46 | 9/3/2026 |
| 1.0.0-prerelease.30 | 42 | 9/3/2026 |
| 1.0.0-prerelease.29 | 52 | 9/3/2026 |
| 1.0.0-prerelease.28 | 44 | 9/2/2026 |
| 1.0.0-prerelease.27 | 57 | 9/1/2026 |
| 1.0.0-prerelease.26 | 88 | 8/29/2026 |
| 1.0.0-prerelease.25 | 74 | 8/27/2026 |
| 1.0.0-prerelease.24 | 63 | 8/19/2026 |
| 1.0.0-prerelease.23 | 70 | 8/19/2026 |
| 1.0.0-prerelease.22 | 65 | 8/19/2026 |
| 1.0.0-prerelease.21 | 63 | 8/18/2026 |
| 1.0.0-prerelease.20 | 59 | 8/18/2026 |
| 1.0.0-prerelease.19 | 80 | 8/17/2026 |
| 1.0.0-prerelease.18 | 74 | 8/16/2026 |
| 1.0.0-prerelease.17 | 75 | 8/14/2026 |
| 1.0.0-prerelease.16 | 63 | 8/13/2026 |