Grinspector 1.0.4

dotnet add package Grinspector --version 1.0.4
                    
NuGet\Install-Package Grinspector -Version 1.0.4
                    
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="Grinspector" Version="1.0.4">
  <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="Grinspector" Version="1.0.4" />
                    
Directory.Packages.props
<PackageReference Include="Grinspector">
  <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 Grinspector --version 1.0.4
                    
#r "nuget: Grinspector, 1.0.4"
                    
#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 Grinspector@1.0.4
                    
#: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=Grinspector&version=1.0.4
                    
Install as a Cake Addin
#tool nuget:?package=Grinspector&version=1.0.4
                    
Install as a Cake Tool

Grinspector ๐ŸŽ„

"You're a mean one, Mr. Private Method..." ๐ŸŽ…

A .NET source generator library that sneaks into your private methods, properties, and fields for testing purposes - just like the Grinch sneaking into Whoville! No more wrestling with reflection strings and binding flags.

Overview ๐Ÿ”

Why let private members hide like the Grinch in his cave? Instead of using clunky reflection:

var method = typeof(Foo).GetMethod("Bar", BindingFlags.NonPublic | BindingFlags.Instance);
method.Invoke(fooInstance, new object[] { 1, 2 });

Use Grinspector:

var inspector = new Foo_Privates(fooInstance);
inspector.Bar(1, 2);

Features ๐ŸŽ

  • ๐ŸŽ„ Strongly-typed access to private instance methods, properties, and fields
  • ๐ŸŒŸ Static member support: Access private static methods, properties, and fields
  • ๐Ÿ—๏ธ Private constructor support: Create instances via private constructors
  • โญ IntelliSense support (even Santa's elves would be jealous)
  • โœจ Compile-time safety
  • ๐ŸŽ… Refactoring-safe: Renaming private members causes compiler errors, not runtime failures
  • ๐Ÿ”” Generated wrapper classes (uses reflection internally)
  • ๐ŸŽ Simple, declarative API

โš ๏ธ A Word of Caution

Needing to test private members is usually a code smell. Like the Grinch himself, this tool exists because sometimes the world isn't perfect.

If you find yourself reaching for Grinspector, consider:

  • Refactoring: Can the private member be extracted into a separate, testable class?
  • Access modifiers: Should it be internal with InternalsVisibleTo instead?
  • Design: Are you testing implementation details instead of behavior?

That said, legacy code exists, tight deadlines happen, and sometimes you need to test the Grinch's cave before you can renovate it. When you're in that situation, Grinspector beats raw reflection. But always prefer proper design over testing private implementation details.

Use responsibly, like spiked eggnog. ๐Ÿฅƒ

Installation ๐Ÿ“ฆ

dotnet add package Grinspector

Unwrap the gift of type-safe private access!

Usage ๐ŸŽ…

Instance Members

  1. Mark your test method/class with the [PrivatesAvailable(typeof(T))] attribute to generate a wrapper class (think of it as your "Naughty List" for private members):
[Fact]
[PrivatesAvailable(typeof(MyClass))]
public void TestPrivateMethods()
{
    var obj = new MyClass();
    var inspector = new MyClass_Privates(obj);
    
    // Access private methods with full type safety
    int result = inspector.Add(5, 3);        // returns 8
    string secret = inspector.GetSecret();   // returns "secret"
    inspector.DoSomething();                 // void method
    
    // Access private properties and fields
    inspector.SomeProperty = "value";        // set private property
    int value = inspector._privateField;     // read private field
}
  1. The target class with private members:
public class MyClass
{
    private int _privateField;
    private string SomeProperty { get; set; }
    
    private int Add(int a, int b) => a + b;
    private string GetSecret() => "secret";
    private void DoSomething() { /* ... */ }
}

Static Members

Access private static members through the generated _Static class:

[Fact]
[PrivatesAvailable(typeof(MyClass))]
public void TestPrivateStaticMembers()
{
    // Access private static methods
    int result = MyClass_Privates_Static.MultiplyBy2(5);  // returns 10
    
    // Access private static fields
    MyClass_Privates_Static._counter = 42;
    int value = MyClass_Privates_Static._counter;
    
    // Access private static properties
    MyClass_Privates_Static.Configuration = "test";
}
public class MyClass
{
    private static int _counter;
    private static string Configuration { get; set; }
    
    private static int MultiplyBy2(int x) => x * 2;
}

Private Constructors

Create instances through private constructors:

[Fact]
[PrivatesAvailable(typeof(Singleton))]
public void TestPrivateConstructor()
{
    // Create instance via private constructor
    var instance = Singleton_Privates_Static.CreateInstance("config", 123);
    
    // Then inspect its private members
    var inspector = new Singleton_Privates(instance);
    inspector.Initialize();
}
public class Singleton
{
    private Singleton(string config, int value)
    {
        // Private constructor logic
    }
    
    private void Initialize() { /* ... */ }
}

The source generator will create:

  • MyClass_Privates class for instance members
  • MyClass_Privates_Static class for static members and constructors

How It Works ๐ŸŽช

Grinspector uses C# source generators to steal... er, inspect your private members:

Source Generator

  1. Scans for test methods/classes decorated with [PrivatesAvailable(typeof(T))]
  2. Analyzes the target type T for private members (instance and static)
  3. Generates wrapper classes:
    • T_Privates for instance members
    • T_Privates_Static for static members and constructors

This provides:

  • Type Safety: The compiler knows about all private members and their signatures
  • IntelliSense: Full IDE support with autocomplete and documentation
  • Refactoring Safety: When you rename a private method/property/field, the generator re-runs and your test code breaks at compile-time with standard compiler errors
  • Opt-In: Only generates wrappers for types you explicitly mark with the attribute
  • Test-Friendly: Designed specifically for testing scenarios where you need to access private implementation details

Note: Generated code uses reflection internally (MethodInfo.Invoke, PropertyInfo.GetValue/SetValue, FieldInfo.GetValue/SetValue), so there is reflection overhead at runtime. The benefit is compile-time type safety and discoverability through IntelliSense.

Building from Source ๐Ÿ—๏ธ

dotnet build

Running Tests ๐Ÿงช

dotnet test

All tests passing? Your heart just grew three sizes! โค๏ธ

Requirements โ„๏ธ

  • .NET 10.0 or later
  • A willingness to peek at private members (we won't judge! ๐Ÿ˜‰)

License ๐Ÿ“œ

MIT - Free as a sleigh ride!

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

    • No dependencies.

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
1.0.4 208 12/19/2025
1.0.3 211 12/19/2025
1.0.2 209 12/19/2025
1.0.1 207 12/19/2025
1.0.0 210 12/19/2025