Making code internal but available for unit testing from other projects
Asked Answered
S

6

164

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?

Spinal answered 20/9, 2008 at 3:10 Comment(1)
possible duplicate of C# "internal" access modifier when doing unit testingDisconformity
C
241

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
{
Cleareyed answered 20/9, 2008 at 3:24 Comment(7)
I'd suggest putting a #if DEBUG around the attribute, and then unit testing in debug. That way you'll be sure that the attribute isnt set on release code.Noblewoman
This is just an idea, I don't know.... How about: #if DEBUG public class IniReader #else internal class IniReader #endif Probably not recommended? Why?Gingrich
Well, why limiting tests to debug builds ?Delaware
Also, just a nitpick, there's no need for the "friend" assemblies to be strongly named (might be a good practice - even if my personal taste says otherwise, but not mandatory).Delaware
Disagree. If I'm building a complex component with a very thin public API, it's unpractical and unrealistic to only test through the public API. You'll end up with an unmaintainble ball of mud. Instead, I'd carefully define the internal units and testing them separately. As Jeremy D. Miller has said quite often: "Test small before you test big".Parve
For .NET Core - add InternalsVisibleTo to any .cs file in the app. See details here - https://mcmap.net/q/67951/-unit-testing-internal-methods-in-vs2017-net-standard-libraryQuartic
What does this actually look like in the unit tests though?Lian
R
19

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/

Rochelle answered 27/4, 2021 at 10:46 Comment(0)
P
4

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.

Plectognath answered 20/9, 2008 at 3:22 Comment(10)
Implementation details should be exercised as part of a encompassing test. Don't peek at private variables... test expected behavior. If the test is right.. all the internal plumbing and wiring should be tested as part of it. Voted up.Hoopes
i dont necessarily agree with this as these classes are "public" to other classes inside the DLL and the functionality of the class should be tested indepdentlySpinal
I also don't agree. Units are units and they must be tested in isolation.Decalogue
Great general guideline, but let's not get dogmatic. Tests serve two main purposes: 1) Regression - make sure you didn't break something, 2) Increasing the speed of development. If I have to stand up a huge service every time I want to write a line of code I will be hampering development. If I have a complex internal piece I want to be able to develop and test in isolation. Conversely, I don't want everything tested in isolation (thus decreasing regression testing value, or duplicating test code), but some code justifies isolation. Perhaps the key is to make it a separate assembly.Huntingdon
My class is internal because it is part of an open library distributed as a nuget assembly - it has a public API of available classes and I want internally used classes to be "internal". Not sure how I need to re-examine this - these classes need to be tested. They are just not public as a service to those understanding what is "available" (ie not internal). You are basically saying that private parts of an API shouldn't be tested - sounds wrong to meFilings
@Filings - Absolutely not. I am advocating for strong test coverage, but by definition, those private parts of the API should get tested because they are used to support the public API. Sufficient tests on the public API will exercise the private member, and if it does not, then you have unnecessary code.Plectognath
@Plectognath that would be integration testing. The internal classes still need unit tests. My case is not the same as testing private class members because my classes are "internal" only as clarification to consumers of the code and to avoid presenting them with classes they don't/can't/shouldn't useFilings
OP here is correct. You're coupling your tests to the internal implementation. Hence your team is forever a slave to mending the tests and horrible mocking code. Test public APIs only, be that packaged lib API or network exposed APIs. All code should be exercisable via the front door. Testing implementation is why TDD has largely died, it's just a huge PITA to test literally every class of the whole app, rather than focus on assuring behaviours of the system. I think almost everyone, including "authoritative" books, have had this wrong or not made it clear.Therefor
If you cannot test FULLY via public classes and methods, accessing internal classes and methods beats poor test coverage. Consider shipping test classes in your code that tests can use to hook into internal code, this is not ideal... But reliable TVs have JTAG connectors inside and reliable cars have a diagnostic ports inside, its common sense - but dont use it to drive the car!.Northrop
I think this answer, although helpful, could use a bit more suggestions on how to approach fixing this issueHang
C
4

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*/});
Cremate answered 23/6, 2012 at 5:45 Comment(0)
L
0

We are in 2023 now and this is now possible with dotnet 8 using UnsafeAccessorAttribute. Before dotnet 8 the solution was either to use the internal keyword (that does not answer the question because internal is different that private) Or using reflection wich is very slow. Now in dotnet 8 you can access private fields without having to use reflection:

https://www.meziantou.net/accessing-private-members-without-reflection-in-csharp.htm

Latakia answered 17/10, 2023 at 4:18 Comment(0)
H
-6

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.

Habited answered 20/9, 2008 at 3:20 Comment(2)
Wait, what? You're saying don't make a public sealed class? What's your reasoning for that gem?Weise
@Weise traumapony has not logged in since 2009.Takara

© 2022 - 2025 — McMap. All rights reserved.