Hertanto Lie
Hertanto Lie

Reputation: 9412

C# "internal" access modifier when doing unit testing

I'm trying to figure out if I should start using more of internal access modifier.

I know that if we use internal and set the assembly variable InternalsVisibleTo, we can test functions that we don't want to declare public from the testing project.

This makes me think that I should just always use internal because at least each project (should?) have its own testing project.

Why shouldn't one do this? When should one use private?

Upvotes: 694

Views: 257683

Answers (11)

Norcino
Norcino

Reputation: 6589

For these working with signed assemblies, this might be helpful. If the project you are making visible is signed, you need to also sign the targeted project and use the following snippet:

  <ItemGroup>
    <AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
      <_Parameter1>[ASSEMBLY_NAME], PublicKey=[PUBLIC_KEY]</_Parameter1>
    </AssemblyAttribute>
  </ItemGroup>

This exposes you assembly and specifies the public key of the signing key.

In the targeted project (ASSEMBLY_NAME), add this instead (which is the same you use in your other project):

<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>[PATH_TO_snk]</AssemblyOriginatorKeyFile>

If you need to find the Public Key please see this SO question: Obtain .NET PublicKeyToken from snk file?

Upvotes: 0

ihebiheb
ihebiheb

Reputation: 5173

Starting with .NET 5, you can use also this syntax in the csproj file of the project being tested:

  <ItemGroup>
    <InternalsVisibleTo Include="MyProject.Tests" />
  </ItemGroup>

Upvotes: 128

EricSchaefer
EricSchaefer

Reputation: 26330

Internal classes need to be tested and there is an assembly attribute:

using System.Runtime.CompilerServices;

[assembly:InternalsVisibleTo("MyTests")]

Add this to the project info file, e.g. Properties\AssemblyInfo.cs, for the project under test. In this case "MyTests" is the test project.

Upvotes: 1547

Amit Sood
Amit Sood

Reputation: 415

For .NET core you can add the attribute to the namespace as [assembly: InternalsVisibleTo("MyUnitTestsAssemblyName")]. e.g. Something like

using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Applications.ExampleApp.Tests")]
namespace Applications.ExampleApp
 internal sealed class ASampleClass : IDisposable
    {
        private const string ApiVersionPath = @"api/v1/";
        ......
        ......
        ......
        }
    }

Upvotes: 4

Black_Rider
Black_Rider

Reputation: 1575

Add InternalsVisibleTo.cs file to project's root folder where .csproj file present.

Content of InternalsVisibleTo.cs should be following

using System.Runtime.CompilerServices;

[assembly: InternalsVisibleTo("AssemblyName.WhichNeedAccess.Example.UnitTests")]

Upvotes: 1

Floating Sunfish
Floating Sunfish

Reputation: 5720

I'm using .NET Core 3.1.101 and the .csproj additions that worked for me were:

<PropertyGroup>
  <!-- Explicitly generate Assembly Info -->
  <GenerateAssemblyInfo>true</GenerateAssemblyInfo>
</PropertyGroup>

<ItemGroup>
  <AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleToAttribute">
  <_Parameter1>MyProject.Tests</_Parameter1>
  </AssemblyAttribute>
</ItemGroup>

Upvotes: 20

balintn
balintn

Reputation: 1413

In .NET Core 2.2, add this line to your Program.cs:

using ...
using System.Runtime.CompilerServices;

[assembly: InternalsVisibleTo("MyAssembly.Unit.Tests")]

namespace
{
...

Upvotes: 5

galdin
galdin

Reputation: 14034

Adding to Eric's answer, you can also configure this in the csproj file:

<ItemGroup>
    <AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
      <_Parameter1>MyTests</_Parameter1>
    </AssemblyAttribute>
</ItemGroup>

Or if you have one test project per project to be tested, you could do something like this in your Directory.Build.props file:

<ItemGroup>
    <AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
      <_Parameter1>$(MSBuildProjectName).Test</_Parameter1>
    </AssemblyAttribute>
</ItemGroup>

See: https://stackoverflow.com/a/49978185/1678053
Example: https://github.com/gldraphael/evlog/blob/master/Directory.Build.props#L5-L12

Upvotes: 217

Brian Rasmussen
Brian Rasmussen

Reputation: 116401

If you want to test private methods, have a look at PrivateObject and PrivateType in the Microsoft.VisualStudio.TestTools.UnitTesting namespace. They offer easy to use wrappers around the necessary reflection code.

Docs: PrivateType, PrivateObject

For VS2017 & 2019, you can find these by downloading the MSTest.TestFramework nuget

Upvotes: 137

Jon Skeet
Jon Skeet

Reputation: 1499840

Keep using private by default. If a member shouldn't be exposed beyond that type, it shouldn't be exposed beyond that type, even to within the same project. This keeps things safer and tidier - when you're using the object, it's clearer which methods you're meant to be able to use.

Having said that, I think it's reasonable to make naturally-private methods internal for test purposes sometimes. I prefer that to using reflection, which is refactoring-unfriendly.

One thing to consider might be a "ForTest" suffix:

internal void DoThisForTest(string name)
{
    DoThis(name);
}

private void DoThis(string name)
{
    // Real implementation
}

Then when you're using the class within the same project, it's obvious (now and in the future) that you shouldn't really be using this method - it's only there for test purposes. This is a bit hacky, and not something I do myself, but it's at least worth consideration.

Upvotes: 11

Steven Behnke
Steven Behnke

Reputation: 3344

You can use private as well and you can call private methods with reflection. If you're using Visual Studio Team Suite it has some nice functionality that will generate a proxy to call your private methods for you. Here's a code project article that demonstrates how you can do the work yourself to unit test private and protected methods:

http://www.codeproject.com/KB/cs/testnonpublicmembers.aspx

In terms of which access modifier you should use, my general rule of thumb is start with private and escalate as needed. That way you will expose as little of the internal details of your class as are truly needed and it helps keep the implementation details hidden, as they should be.

Upvotes: 10

Related Questions