178

I am currently playing around with the latest Visual Studio 2017 Release Candidate by creating a .NET Standard 1.6 library. I am using xUnit to unit test my code and was wondering if you can still test internal methods in VS2017.

I remember that you could add a line in AssemblyInfo.cs class in VS2015 that would enable specified projects to see internal methods:

[assembly:InternalsVisibleTo("MyTests")]

As there is no AssemblyInfo.cs class in VS2017 .NET Standard projects, I was wondering if you can still unit test internal methods?

Pang
  • 9,564
  • 146
  • 81
  • 122
Phil Murray
  • 6,396
  • 9
  • 45
  • 95
  • 3
    You *should* be able to unit test your code from externally-visible functionality alone. After all, if no logical path from external code can reach those internal methods then what would they be doing there in the first place? – David Feb 14 '17 at 20:18
  • 4
    @David I can and have done this but I have put simple unit tests around some internal classes previously. Just to be more explicit in the testing. – Phil Murray Feb 14 '17 at 20:21
  • 5
    AFAIK, you can place this attribute in any other file, outside the `namespace` block, and it should compile. There shouldn't be anything magical about `AssemblyInfo.cs`. Doesn't it work? Of course, you need to add the correct `using` clause or use the fully qualified attribute `[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Something")]`. – vgru Feb 14 '17 at 20:25
  • 3
    @David If you are creating a library with internal classes and you need to test and mock these classes, `InternalsVisibleTo` is critical - eg here - https://stackoverflow.com/a/17574183/43453 – PandaWood Sep 08 '18 at 03:42

5 Answers5

257

According to .NET docs for the InternalsVisibleToAttribute:

The attribute is applied at the assembly level. This means that it can be included at the beginning of a source code file, or it can be included in the AssemblyInfo file in a Visual Studio project.

In other words, you can simply place it in your own arbitrarily named .cs file, and it should work fine:

// some .cs file included in your project
using System.Runtime.CompilerServices;
[assembly:InternalsVisibleTo("MyTests")]
Pang
  • 9,564
  • 146
  • 81
  • 122
vgru
  • 49,838
  • 16
  • 120
  • 201
  • 1
    @PhilMurray: also, it seems there is a setting that should allow you to create a "classic" `AssemblyInfo.cs` file like explained [here](http://stackoverflow.com/a/42183749/69809). Otherwise all the attributes like "description", "copyright" and other stuff get stored inside the .csproj file. – vgru Feb 14 '17 at 20:31
94

As described here:

https://blog.sanderaernouts.com/make-internals-visible-with-new-csproj-format

It is possible to add the internal visible attribute within the project file by adding another ItemGroup:

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

or even:

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

I like that solution because the project file seems to be the right place for defining such concerns.

JanDotNet
  • 3,746
  • 21
  • 30
  • I prefer this solution and totally agree that the `.csproj` seems to be the right place – Sebastian Nov 23 '21 at 08:55
  • Thanks for this suggestion! Better to have it in the .csproj file when the AssemblyInfo.cs file seems to have been deprecated and most people don't like assembly directives in some random .cs file. I didn't like adding it, and other reviewers wouldn't even consider keeping it, lol. So .csproj file is the perfect spot. – Torrents Feb 17 '22 at 00:15
  • If you are using Moq, you will need to add another AssemblyAttribute in this ItemGroup with the same Include, but with `<_Parameter1>DynamicProxyGenAssembly2`. – ScubaSteve Feb 19 '22 at 03:20
8

While the first answer is perfectly fine. If you feel you still want to do this in the original AssemblyInfo you can always choose to not auto generate the file and add it manually.

<PropertyGroup>
   <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup>

For more information: https://stackoverflow.com/a/47075759/869033

Nick N.
  • 12,902
  • 7
  • 57
  • 75
5

The "InternalsVisibleTo" attribute is key to any sort of "white-box" (the term of the decade, I guess) testing for .Net. It can be placed in any c# file with the "assembly" attribute on the front. Note that MS DOCs say that the assembly name must be qualified by the public key token, if it is signed. Sometimes that does not work and one must use the full public key in it's place. Access to internals is key to testing concurrent systems and in many other situations. See https://www.amazon.com/xUnit-Test-Patterns-Refactoring-Code/dp/0131495054. In this book, Meszaros describes a variety of coding styles that basically constitute a "Design For Test" approach to program development. At least that's the way I've used it over the years.

ADDED: Sorry, I haven't been on here for a while. One approach is called the "testing subclass" approach by Meszaros. Again, one has to use "internalsvisableto" to access the base class's internals. This is a great solution, but it doesn't work for sealed classes. When I teach "Design For Test", I suggest that it's one of the things that are required to be "pre-engineered" into the base classes in order to provide testability. It has to become almost a cultural thing. Design a "base" base class that is unsealed. Call it UnsealedBaseClass or something uniformly recognizable. This is the class to be subclassed for testing. It is also subclassed to build the production sealed class, which often only differs in the constructors it exposes. I work in the nuclear industry and the testing requirements are taken VERY seriously. So, I have to think about these things all the time. By the way, leaving testing hooks in production code is not considered a problem in our field, as long as they are "internal" in a .Net implementation. The ramifications of NOT testing something can be quite profound.

kurt.matis
  • 61
  • 1
  • 3
1

Another way is to use a 'wrapper' TestMyFoo public class inside the target project that has public methods and inherits from the class you need to test (e.g. MyFoo). These public methods simply call through onto the base class you want to test.

It is not 'ideal' as you end up shipping a test hook in your target project. But consider modern reliable cars ship with diagnostic ports and modern reliable electronics ship with a JTAG connection. But nobody is silly enough to drive their car using the diagnostic port.

Pang
  • 9,564
  • 146
  • 81
  • 122
andrew pate
  • 3,833
  • 36
  • 28