FractalDataWorks.CodeBuilder.Analysis.CSharp 0.6.0-rc.1

This is a prerelease version of FractalDataWorks.CodeBuilder.Analysis.CSharp.
dotnet add package FractalDataWorks.CodeBuilder.Analysis.CSharp --version 0.6.0-rc.1
                    
NuGet\Install-Package FractalDataWorks.CodeBuilder.Analysis.CSharp -Version 0.6.0-rc.1
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="FractalDataWorks.CodeBuilder.Analysis.CSharp" Version="0.6.0-rc.1">
  <PrivateAssets>all</PrivateAssets>
  <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="FractalDataWorks.CodeBuilder.Analysis.CSharp" Version="0.6.0-rc.1" />
                    
Directory.Packages.props
<PackageReference Include="FractalDataWorks.CodeBuilder.Analysis.CSharp">
  <PrivateAssets>all</PrivateAssets>
  <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add FractalDataWorks.CodeBuilder.Analysis.CSharp --version 0.6.0-rc.1
                    
#r "nuget: FractalDataWorks.CodeBuilder.Analysis.CSharp, 0.6.0-rc.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package FractalDataWorks.CodeBuilder.Analysis.CSharp@0.6.0-rc.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=FractalDataWorks.CodeBuilder.Analysis.CSharp&version=0.6.0-rc.1&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=FractalDataWorks.CodeBuilder.Analysis.CSharp&version=0.6.0-rc.1&prerelease
                    
Install as a Cake Tool

FractalDataWorks.CodeBuilder.Analysis.CSharp

C# implementation of the CodeBuilder analysis framework using Roslyn. Provides compilation verification and syntax expectations for testing generated C# code.

Overview

This package implements the language-agnostic analysis interfaces defined in FractalDataWorks.CodeBuilder.Analysis using Microsoft's Roslyn compiler platform. It enables comprehensive testing of generated C# code including compilation verification, syntax structure validation, and runtime method execution.

Installation

dotnet add package FractalDataWorks.CodeBuilder.Analysis.CSharp

Core Components

CSharpCompilationVerifier

Implements ICompilationVerifier to verify that generated C# code compiles correctly:

using FractalDataWorks.CodeBuilder.Testing.CSharp;

var verifier = new CSharpCompilationVerifier();

// Basic compilation verification
var result = verifier.CompileAndVerify(@"
    public class TestClass
    {
        public string Name { get; set; }
        public int Calculate() => 42;
    }");

Assert.True(result.Success);
Assert.Empty(result.Diagnostics.Where(d => d.Severity == DiagnosticSeverity.Error));

CSharpSyntaxExpectations

Implements ISyntaxExpectations to provide fluent syntax verification:

using FractalDataWorks.CodeBuilder.Testing.CSharp;

var syntaxExpectations = new CSharpSyntaxExpectations();

syntaxExpectations
    .ExpectCode(generatedCode)
    .HasClass("TestClass", c => c
        .IsPublic()
        .HasProperty("Name", p => p
            .HasType("string")
            .HasGetter()
            .HasSetter())
        .HasMethod("Calculate", m => m
            .IsPublic()
            .HasReturnType("int")))
    .Compiles();

Features

Compilation Verification

  • Full Compilation: Compiles source code to assemblies using Roslyn
  • Custom Options: Support for different output kinds, optimization levels, and references
  • Diagnostic Reporting: Detailed error and warning information with file locations
  • Method Execution: Invoke methods from compiled assemblies for integration testing

Syntax Expectations

  • Structure Validation: Verify namespaces, classes, interfaces, enums, records
  • Member Validation: Assert methods, properties, fields, constructors
  • Modifier Validation: Check access modifiers, static, abstract, sealed, etc.
  • Type Validation: Verify parameter types, return types, property types
  • Fluent API: Chainable expectations for readable test code

Usage Examples

Basic Compilation Testing

[Test]
public void GeneratedCode_ShouldCompileSuccessfully()
{
    var generatedCode = @"
        namespace MyApp
        {
            public class Person
            {
                public string Name { get; set; }
                public int Age { get; set; }
                
                public string GetDisplayName()
                {
                    return $""{Name} ({Age})"";
                }
            }
        }";

    var verifier = new CSharpCompilationVerifier();
    var result = verifier.CompileAndVerify(generatedCode);
    
    Assert.True(result.Success);
    Assert.NotNull(result.AssemblyBytes);
}

Advanced Compilation with Options

[Test]
public void GeneratedCode_WithCustomOptions_ShouldCompile()
{
    var options = new AnalysisOptions
    {
        OutputKind = OutputKind.DynamicallyLinkedLibrary,
        OptimizationLevel = OptimizationLevel.Release,
        AllowUnsafe = false,
        References = new[]
        {
            typeof(System.Collections.Generic.IEnumerable<>).Assembly.Location,
            typeof(System.ComponentModel.INotifyPropertyChanged).Assembly.Location
        }
    };

    var result = verifier.CompileWithOptions(new[] { generatedCode }, options);
    Assert.True(result.Success);
}

Method Execution Testing

[Test]
public void CompiledCode_ShouldExecuteCorrectly()
{
    var sourceCode = @"
        public class Calculator
        {
            public static int Add(int a, int b)
            {
                return a + b;
            }
            
            public int Multiply(int a, int b)
            {
                return a * b;
            }
        }";

    var verifier = new CSharpCompilationVerifier();
    var result = verifier.CompileAndVerify(sourceCode);
    
    // Test static method
    var sum = result.InvokeMethod("Calculator", "Add", 5, 3);
    Assert.Equal(8, sum);
    
    // Test instance method
    var product = result.InvokeMethod("Calculator", "Multiply", 4, 6);
    Assert.Equal(24, product);
}

Comprehensive Syntax Validation

[Test]
public void GeneratedClass_ShouldHaveExpectedStructure()
{
    var generatedCode = @"
        namespace MyApp.Models
        {
            public abstract class BaseEntity
            {
                public int Id { get; set; }
                public DateTime CreatedAt { get; init; }
                
                protected BaseEntity(int id)
                {
                    Id = id;
                    CreatedAt = DateTime.UtcNow;
                }
                
                public abstract void Validate();
            }
            
            public class User : BaseEntity
            {
                public string Name { get; set; }
                public string Email { get; set; }
                
                public User(int id, string name, string email) : base(id)
                {
                    Name = name;
                    Email = email;
                }
                
                public override void Validate()
                {
                    if (string.IsNullOrEmpty(Name))
                        throw new ArgumentException(""Name is required"");
                    if (string.IsNullOrEmpty(Email))
                        throw new ArgumentException(""Email is required"");
                }
                
                public static User Create(string name, string email)
                {
                    return new User(0, name, email);
                }
            }
        }";

    var syntaxExpectations = new CSharpSyntaxExpectations();
    syntaxExpectations
        .ExpectCode(generatedCode)
        .HasNamespace("MyApp.Models", ns => ns
            .HasClass("BaseEntity", c => c
                .IsPublic()
                .IsAbstract()
                .HasProperty("Id", p => p
                    .HasType("int")
                    .HasGetter()
                    .HasSetter())
                .HasProperty("CreatedAt", p => p
                    .HasType("DateTime")
                    .HasGetter()
                    .HasInitSetter())
                .HasConstructor(ctor => ctor
                    .HasParameter("id", "int"))
                .HasMethod("Validate", m => m
                    .IsPublic()
                    .IsAbstract()))
            .HasClass("User", c => c
                .IsPublic()
                .InheritsFrom("BaseEntity")
                .HasProperty("Name", p => p.HasType("string"))
                .HasProperty("Email", p => p.HasType("string"))
                .HasConstructor(ctor => ctor
                    .HasParameter("id", "int")
                    .HasParameter("name", "string")
                    .HasParameter("email", "string"))
                .HasMethod("Validate", m => m
                    .IsPublic()
                    .IsOverride())
                .HasMethod("Create", m => m
                    .IsPublic()
                    .IsStatic()
                    .HasReturnType("User")
                    .HasParameter("name", "string")
                    .HasParameter("email", "string"))))
        .Compiles();
}

Interface Validation

[Test]
public void GeneratedInterface_ShouldHaveCorrectMembers()
{
    var interfaceCode = @"
        public interface IRepository<T> where T : class
        {
            Task<T?> GetByIdAsync(int id);
            Task<IEnumerable<T>> GetAllAsync();
            Task<T> AddAsync(T entity);
            Task UpdateAsync(T entity);
            Task DeleteAsync(int id);
            
            IQueryable<T> Query { get; }
            int Count { get; }
        }";

    var syntaxExpectations = new CSharpSyntaxExpectations();
    syntaxExpectations
        .ExpectCode(interfaceCode)
        .HasInterface("IRepository", i => i
            .HasMethod("GetByIdAsync", m => m
                .HasReturnType("Task<T?>")
                .HasParameter("id", "int"))
            .HasMethod("GetAllAsync", m => m
                .HasReturnType("Task<IEnumerable<T>>"))
            .HasMethod("AddAsync", m => m
                .HasReturnType("Task<T>")
                .HasParameter("entity", "T"))
            .HasProperty("Query", p => p
                .HasType("IQueryable<T>"))
            .HasProperty("Count", p => p
                .HasType("int")))
        .Compiles();
}

Record and Enum Validation

[Test]
public void GeneratedRecordAndEnum_ShouldHaveCorrectStructure()
{
    var code = @"
        public enum Status
        {
            Pending = 1,
            Active = 2,
            Inactive = 3
        }
        
        public record PersonDto(int Id, string Name, Status Status)
        {
            public DateTime CreatedAt { get; init; } = DateTime.UtcNow;
            
            public string GetStatusDisplay() => Status switch
            {
                Status.Pending => ""Pending Approval"",
                Status.Active => ""Active"",
                Status.Inactive => ""Inactive"",
                _ => ""Unknown""
            };
        }";

    var syntaxExpectations = new CSharpSyntaxExpectations();
    syntaxExpectations
        .ExpectCode(code)
        .HasEnum("Status", e => e
            .HasValue("Pending", 1)
            .HasValue("Active", 2)
            .HasValue("Inactive", 3))
        .HasRecord("PersonDto", r => r
            .HasParameter("Id", "int")
            .HasParameter("Name", "string")
            .HasParameter("Status", "Status")
            .HasProperty("CreatedAt", p => p
                .HasType("DateTime")
                .HasInitSetter())
            .HasProperty("GetStatusDisplay", p => p
                .HasType("string")))
        .Compiles();
}

Error Handling and Diagnostics

[Test]
public void InvalidCode_ShouldProvideDetailedDiagnostics()
{
    var invalidCode = @"
        public class InvalidClass
        {
            public void InvalidMethod()
            {
                // This will cause a compilation error
                UnknownType variable = new UnknownType();
            }
        }";

    var verifier = new CSharpCompilationVerifier();
    var result = verifier.CompileAndVerify(invalidCode);
    
    Assert.False(result.Success);
    
    var errors = result.Diagnostics.Where(d => d.Severity == DiagnosticSeverity.Error);
    Assert.NotEmpty(errors);
    
    var error = errors.First();
    Assert.Contains("UnknownType", error.Message);
    Assert.NotNull(error.Line);
    Assert.NotNull(error.Column);
}

Testing Source Generators

This package is particularly useful for testing Roslyn source generators:

[Test]
public void SourceGenerator_ShouldGenerateExpectedCode()
{
    // Arrange
    var inputCode = @"
        [AutoProperty]
        public partial class Person
        {
            private string _name;
            private int _age;
        }";

    // Act
    var generatedCode = RunSourceGenerator<AutoPropertyGenerator>(inputCode);
    
    // Assert
    var verifier = new CSharpCompilationVerifier();
    var syntaxExpectations = new CSharpSyntaxExpectations();
    
    // Verify it compiles
    var result = verifier.CompileAndVerify(inputCode, generatedCode);
    Assert.True(result.Success);
    
    // Verify structure
    syntaxExpectations
        .ExpectCode(generatedCode)
        .HasClass("Person", c => c
            .IsPublic()
            .IsPartial()
            .HasProperty("Name", p => p
                .HasType("string")
                .HasGetter()
                .HasSetter())
            .HasProperty("Age", p => p
                .HasType("int")
                .HasGetter()
                .HasSetter()));
}

Best Practices

  1. Always Verify Compilation: Use CompileAndVerify to ensure generated code is syntactically correct
  2. Test Structure and Behavior: Use syntax expectations for structure and method invocation for behavior
  3. Handle Diagnostics: Check compilation diagnostics for warnings and errors
  4. Use Appropriate References: Include necessary assembly references for complex scenarios
  5. Test Edge Cases: Verify error handling and boundary conditions
  6. Isolated Tests: Keep tests independent and focused on specific scenarios

Integration with Testing Frameworks

This package works well with popular .NET testing frameworks:

xUnit

public class CodeGenerationTests
{
    private readonly CSharpCompilationVerifier _verifier = new();
    private readonly CSharpSyntaxExpectations _syntaxExpectations = new();
    
    [Fact]
    public void ShouldGenerateValidCode() { /* test code */ }
}

NUnit

[TestFixture]
public class CodeGenerationTests
{
    private CSharpCompilationVerifier _verifier;
    private CSharpSyntaxExpectations _syntaxExpectations;
    
    [SetUp]
    public void Setup()
    {
        _verifier = new CSharpCompilationVerifier();
        _syntaxExpectations = new CSharpSyntaxExpectations();
    }
    
    [Test]
    public void ShouldGenerateValidCode() { /* test code */ }
}

MSTest

[TestClass]
public class CodeGenerationTests
{
    private CSharpCompilationVerifier _verifier;
    private CSharpSyntaxExpectations _syntaxExpectations;
    
    [TestInitialize]
    public void Initialize()
    {
        _verifier = new CSharpCompilationVerifier();
        _syntaxExpectations = new CSharpSyntaxExpectations();
    }
    
    [TestMethod]
    public void ShouldGenerateValidCode() { /* test code */ }
}

Dependencies

  • Microsoft.CodeAnalysis.CSharp: For Roslyn compiler services
  • FractalDataWorks.CodeBuilder.Analysis: For language-agnostic interfaces
  • .NET Standard 2.0: Compatible with .NET Framework 4.6.1+ and .NET Core 2.0+

License

Apache License 2.0 - see the LICENSE file for details.

Product 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 was computed.  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 was computed.  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 was computed.  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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.6.0-rc.1 59 2/9/2026
Loading failed