Making code internal but available for unit testing from other projects

C#Unit TestingScope

C# Problem Overview


We put all of our unit tests in their own projects. We find that we have to make certain classes public instead of internal just for the unit tests. Is there anyway to avoid having to do this. What are the memory implication by making classes public instead of sealed?

C# Solutions


Solution 1 - C#

If you're using .NET, the InternalsVisibleTo assembly attribute allows you to create "friend" assemblies. These are specific strongly named assemblies that are allowed to access internal classes and members of the other assembly.

Note, this should be used with discretion as it tightly couples the involved assemblies. A common use for InternalsVisibleTo is for unit testing projects. It's probably not a good choice for use in your actual application assemblies, for the reason stated above.

Example:

[assembly: InternalsVisibleTo("NameAssemblyYouWantToPermitAccess")]
namespace NameOfYourNameSpace
{

Solution 2 - C#

If it is an internal class then it must not be getting used in isolation. Therefore you shouldn't really be testing it apart from testing some other class that makes use of that object internally.

Just as you shouldn't test private members of a class, you shouldn't be testing internal classes of a DLL. Those classes are implementation details of some publicly accessible class, and therefore should be well exercised through other unit tests.

The idea is that you only want to test the behavior of a class because if you test internal implementation details then your tests will be brittle. You should be able to change the implementation details of any class without breaking all your tests.

If you find that you really need to test that class, then you might want to reexamine why that class is internal in the first place.

Solution 3 - C#

for documentation purposes

alternatively you can instantiate internal class by using Type.GetType method

example

//IServiceWrapper is public class which is 
//the same assembly with the internal class 
var asm = typeof(IServiceWrapper).Assembly;
//Namespace.ServiceWrapper is internal
var type = asm.GetType("Namespace.ServiceWrapper");
return (IServiceWrapper<T>)Activator
    .CreateInstance(type, new object[1] { /*constructor parameter*/ });

for generic type there are different process as bellow:

var asm = typeof(IServiceWrapper).Assembly;
//note the name Namespace.ServiceWrapper`1
//this is for calling Namespace.ServiceWrapper<>
var type = asm.GetType("Namespace.ServiceWrapper`1");
var genType = type.MakeGenericType(new Type[1] { typeof(T) });
return (IServiceWrapper<T>)Activator
     .CreateInstance(genType, new object[1] { /*constructor parameter*/});

Solution 4 - C#

Below are ways to use in .NET Core applications.

  1. Add AssemblyInfo.cs file and add [assembly: InternalsVisibleTo("AssemblytoVisible")]
  2. Add this in .csproj file (the project which contains the Internal classes)
<ItemGroup>
  <AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
    <_Parameter1>Test_Project_Name</_Parameter1> <!-- The name of the project that you want the Internal class to be visible To it -->
  </AssemblyAttribute>
</ItemGroup>

For more information please follow https://improveandrepeat.com/2019/12/how-to-test-your-internal-classes-in-c/

Solution 5 - C#

Classes can be both public AND sealed.

But, don't do that.

You can create a tool to reflect over internal classes, and emit a new class that accesses everything via reflection. MSTest does that.

Edit: I mean, if you don't want to include -any- testing stuff in your original assembly; this also works if the members are private.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionleoraView Question on Stackoverflow
Solution 1 - C#AshView Answer on Stackoverflow
Solution 2 - C#JoshView Answer on Stackoverflow
Solution 3 - C#ktutnikView Answer on Stackoverflow
Solution 4 - C#babula pradhanView Answer on Stackoverflow
Solution 5 - C#TraumaPonyView Answer on Stackoverflow