C# "internal" access modifier when doing unit testing
I'm new to unit testing and 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. Can you guys tell me why I shouldn't do this? When should I use private
?
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.
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
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
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>
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.