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
?
System.Diagnostics.Debug.Assert()
within the methods themselves.
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
AssemblyInfo
file.
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>
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.
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.
ForTest
approach but I always find it dead ugly (adding code which provides no actual value in terms of production business logic). Usually I find I had to use the approach because the design is somwhat unfortunate (i.e. having to reset singleton instances between tests)
ForTest
is obviously wrong, whereas if you just make the method internal it looks like it's fine to use.
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/";
......
......
......
}
}
In .NET Core 2.2, add this line to your Program.cs:
using ...
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("MyAssembly.Unit.Tests")]
namespace
{
...
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")]
Success story sharing
private
, but too manyprivate
things might very well point to aninternal
class that is struggling to be extracted. TDD or no TDD, I prefer having more tests that test a lot of code, than to have few test that exercise the same amount of code. And avoiding to testinternal
stuff doesn't exactly help to achieve a good ratio.#if DEBUG
,#endif
block will enable this option only in debug builds.