How to unit test SourceGenerator?
Asked Answered
C

3

6

I writed a SourceGenerator, but how do I test it?

Main issue is how to imitate GeneratorExecutionContext (or just Compilation inside it) which generator gets into Execute method. I think there is a proper way to make fake SyntaxTrees for unit testing, but I cant find it. There are many articles about source generators itself, but none of them explain how to test generators.

Consequence answered 12/4, 2021 at 6:56 Comment(0)
S
5

You should look at official Source Generators Cookbook

There is example from it:

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Diagnostics;
using System.Linq;
using System.Reflection;

namespace GeneratorTests.Tests
{
    [TestClass]
    public class GeneratorTests
    {
        [TestMethod]
        public void SimpleGeneratorTest()
        {
            // Create the 'input' compilation that the generator will act on
            Compilation inputCompilation = CreateCompilation(@"
namespace MyCode
{
    public class Program
    {
        public static void Main(string[] args)
        {
        }
    }
}
");

            // directly create an instance of the generator
            // (Note: in the compiler this is loaded from an assembly, and created via reflection at runtime)
            CustomGenerator generator = new CustomGenerator();

            // Create the driver that will control the generation, passing in our generator
            GeneratorDriver driver = CSharpGeneratorDriver.Create(generator);

            // Run the generation pass
            // (Note: the generator driver itself is immutable, and all calls return an updated version of the driver that you should use for subsequent calls)
            driver = driver.RunGeneratorsAndUpdateCompilation(inputCompilation, out var outputCompilation, out var diagnostics);

            // We can now assert things about the resulting compilation:
            Debug.Assert(diagnostics.IsEmpty); // there were no diagnostics created by the generators
            Debug.Assert(outputCompilation.SyntaxTrees.Count() == 2); // we have two syntax trees, the original 'user' provided one, and the one added by the generator
            Debug.Assert(outputCompilation.GetDiagnostics().IsEmpty); // verify the compilation with the added source has no diagnostics

            // Or we can look at the results directly:
            GeneratorDriverRunResult runResult = driver.GetRunResult();

            // The runResult contains the combined results of all generators passed to the driver
            Debug.Assert(runResult.GeneratedTrees.Length == 1);
            Debug.Assert(runResult.Diagnostics.IsEmpty);

            // Or you can access the individual results on a by-generator basis
            GeneratorRunResult generatorResult = runResult.Results[0];
            Debug.Assert(generatorResult.Generator == generator);
            Debug.Assert(generatorResult.Diagnostics.IsEmpty);
            Debug.Assert(generatorResult.GeneratedSources.Length == 1);
            Debug.Assert(generatorResult.Exception is null);
        }

        private static Compilation CreateCompilation(string source)
            => CSharpCompilation.Create("compilation",
                new[] { CSharpSyntaxTree.ParseText(source) },
                new[] { MetadataReference.CreateFromFile(typeof(Binder).GetTypeInfo().Assembly.Location) },
                new CSharpCompilationOptions(OutputKind.ConsoleApplication));
    }
}
Signally answered 12/4, 2021 at 7:47 Comment(3)
Thank you, this is definetly help. I've already seen that page, but the following code (I dont know how) does not attract my attention. Anyway that's what I looking for.Consequence
The roslyn cookbook is being updated. The approach I recommend is using the testing library. Please see github.com/dotnet/roslyn/blob/… which has the updated approach (not yet merged into main branch).Syracuse
I've already done with my tests, but I definetly check out this updates later on. Thank you.Consequence
F
3

In addition to the Source Generators Cookbook mentioned in the other answer:

The cookbook solution allows you to generate some code and then compare your results to expected, also check for warnings and compilation exceptions etc.

Now, you can additionally EXECUTE the generated code to make sure it's running correctly. For that change the project reference in the test-project like this:

<ProjectReference Include="..\MyGenerator\MyGenerator.csproj"
    ReferenceOutputAssembly="true"
    OutputItemType="Analyzer" />

And then simply call the generated code from your unit tests, like you would in the consumer project.

Foofaraw answered 11/10, 2022 at 19:40 Comment(0)
K
1

I found it was simpler to write tests that tested the generated code.

Say your generator creates classes, and our example scenario is that it will generate a class GeneratedClass. The test project will reference the generator project/package which generates the class in the test project and the tests assert the expected properties/behaviors of that class.

[Fact]
public void GeneratedClass_Should_DoSomething()
{
   var generatedClass = new GeneratedClass();

   // Assert behavior of GeneratedClass
}

Obviously this doesn't cover scenarios like exception handling, but it's effective at testing the happy paths.

Keslie answered 21/3, 2023 at 5:34 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.