Using InternalsVisibleTo in C# Unit Testing

This article provides an overview of the InternalsVisibleTo attribute in C#, explaining how it grants unit test projects access to internal types and members without compromising public API design. You will learn the mechanics behind assembly friendship, modern configuration techniques in .NET project files, how to handle strongly-named assemblies, and best practices for testing non-public code.

What Is InternalsVisibleTo?

In C#, the internal access modifier restricts visibility to code within the same assembly. However, unit tests typically live in a separate test assembly. By default, this prevents unit tests from instantiating internal classes or invoking internal methods directly.

The InternalsVisibleTo attribute, located in the System.Runtime.CompilerServices namespace, resolves this limitation. It designates one or more assemblies as "friend assemblies," granting them access to all internal types and members of the target assembly while keeping those elements hidden from regular consumers and external libraries.

Why Use It in Unit Testing?

Exposing implementation details using the public modifier solely to make them testable degrades library design and pollutes the public API surface. Using InternalsVisibleTo maintains proper encapsulation:

  • Encapsulation Protection: Keeps internal business rules, data-access helpers, and pipeline handlers hidden from third-party consumers.
  • Refactoring Safety: Internal components can be tested in isolation before being integrated into broader workflows.
  • Cleaner Public APIs: Consumers only see the intended entry points of your library or application.

How to Configure InternalsVisibleTo

There are two primary ways to expose internal members to a test assembly in modern .NET projects.

1. In the Project File (.csproj)

In .NET 5 and later, the recommended approach is adding the attribute directly inside the production project’s .csproj file. This eliminates the need for manual AssemblyInfo.cs files:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <InternalsVisibleTo Include="MyApplication.UnitTests" />
  </ItemGroup>

</Project>

2. In an AssemblyInfo.cs File

Alternatively, you can apply the attribute at the assembly level in a C# source file (conventionally named AssemblyInfo.cs):

using System.Runtime.CompilerServices;

[assembly: InternalsVisibleTo("MyApplication.UnitTests")]

Signed (Strong-Named) Assemblies

If your production assembly is signed with a strong name key (.snk), the friend assembly declared in InternalsVisibleTo must also be signed, and you must include the full public key (not the public key token) in the declaration:

[assembly: InternalsVisibleTo("MyApplication.UnitTests, PublicKey=002400000480000094000000060200000024000052534131...")]

To extract the full public key string, use the Strong Name tool (sn.exe) provided with the .NET SDK:

sn -tp MyApplication.UnitTests.dll

Best Practices

  • Test Behaviors Over Pure State: Use InternalsVisibleTo for cohesive, internal architectural components (such as an internal domain service or complex internal parser), not merely to inspect trivial private fields.
  • Avoid Exposing to Multiple Production Assemblies: Keep the friend relationship reserved almost exclusively for test assemblies to prevent tight coupling between distinct production components.
  • Consider Visible Internal Interfaces: If a class is difficult to test, consider abstracting it behind an internal interface that can be easily mocked in your test project using mocking frameworks like Moq or NSubstitute.