Course contentsShow
← C#
Lesson 1707 of 1,90815. Software Quality and TestingFree lesson

Testing AOT Compilations

Validate that reflection-heavy code works correctly in Ahead-of-Time modes.

In this lesson, you will learn how to validate that your application remains functional when using Native Ahead-of-Time (AOT) compilation, specifically by identifying and testing problematic reflection-heavy patterns.

The AOT Challenge

When you publish an application using PublishAot, the compiler performs "tree shaking" to trim unused code and converts C# into native machine code. Because this process happens at build time, it struggles with reflection. If your code dynamically inspects types or calls members that the compiler thinks are unused, those members might be stripped away, causing your application to crash with a MissingMethodException or similar runtime errors.

Identifying Reflection Risks

To test this, you must treat your AOT-compiled binary as a first-class test target. Since you cannot easily debug native binaries, your goal is to trigger the "Reflection paths" during your test suite.

Consider this reflection-heavy service:

public class DynamicService {
    public void Execute(string typeName) {
        var type = Type.GetType(typeName); // Risky in AOT
        var instance = Activator.CreateInstance(type!); 
        // ... invoke methods
    }
}

Testing Strategy

You cannot rely solely on standard unit tests because they run on the JIT-compiled CLR. To effectively validate AOT compatibility:

  1. Publish for AOT in CI: Configure your build pipeline to run a dotnet publish -c Release -r <RID> /p:PublishAot=true step.
  2. Integration Smoke Tests: Execute the resulting native executable against a suite of integration tests that specifically target the paths using reflection. If a type isn't marked with [DynamicDependency] or protected by DynamicallyAccessedMembers attributes, your integration tests will fail during the test run because the necessary metadata was pruned.
  3. Use Trimming Analyzers: Ensure your project has <EnableTrimAnalyzer>true</EnableTrimAnalyzer> enabled. This forces the compiler to warn you about code that might break in AOT. Your tests should treat these warnings as errors to ensure no new reflection debt is introduced.

By treating the AOT-published artifact as the subject of your integration tests, you force the runtime to encounter the reflection paths, surfacing missing metadata issues before they ever reach production.

Key Takeaway: AOT compilation prunes "hidden" code paths; you must validate your application by running your test suite against the actual native binary to ensure reflection-based dynamic lookups remain intact.