74

I would like to write a class library which creates for me a complex object but should only be exposed as little as possible. I want it to be included into other projects and there I only have one call to this library which e.g. returns me an object of a internally created class. I don't want to allow others to create these objects explicitly, but still I want to create a test project for this class library.

For example:

var result = Manager.Instance.Create(definition)

This should be the only access to the class library.

Based on the definition parameter it uses different sub classes to create the requested instance and sets its properties accordingly. Therefore I want to assure somehow by tests that the whole creation process worked fine. But since I also don't want to expose very little internal properties of the result object too I cannot test by only using this public access method since I don't have any properties to assert on.

I know that you should not test for internal mechanics and it is typically bad design and I also was reading through this article, but isn't there maybe any way to create a library plus unit test project and maybe afterwards limit the access to this class? with a wrapper or something?

Mustafa Özçetin
  • 1,893
  • 1
  • 14
  • 16
Thomas Mondel
  • 1,944
  • 3
  • 20
  • 25

3 Answers3

163

In .NET, you can use the InternalsVisibleToAttribute in your class library to make your internal types visible to your unit test project.

That way, you can keep your class internal and still use it from other assemblies that you give access.

You use it like this:

[assembly:InternalsVisibleTo("NameOfYourUnitTestProject")]
Pang
  • 9,564
  • 146
  • 81
  • 122
Wouter de Kort
  • 39,090
  • 12
  • 84
  • 103
  • 15
    Great solution! Some extra info: 1) In case this `[assembly: ...]` syntax doesn't ring a bell: these are Assembly Attributes ( http://msdn.microsoft.com/en-us/library/4w8c1y2s.aspx ), the most well-known of which are the `AssemblyVersionAttribute` and its brothers. 2) You can use these in any .cs file in your project, outside a namespace, but typically, they are specified in the `AssemblyInfo` file located in the 'Properties' project folder. 3) You must indeed specify the *name* of your project, not the *namespace*, in case these differ. – Vincent Sels May 09 '14 at 06:57
  • That's awesome, I had no idea one could do this, you learn something new every day! – MaYaN Jan 19 '16 at 11:20
  • 1
    Thanks. The name for the test project was all I was missing. I found a lot of examples not explaining what string to put into the assembly command. Inputting the name of my test project did the trick! – Zeliax Nov 13 '17 at 10:25
  • I am wondering, what happen if someone create a project with the name NameOfYourUnitTestProject? could it have access to this internal resources? I am thinking from a security point of view. – Álvaro García Oct 25 '22 at 07:57
20

For the latest csproj 2017 formated projects, if your project does not have the AssemblyInfo.cs file, you can add the following setting:

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

You also can use other variables to relplace MSBuildProjectName such as AssemblyName or use the unittest project name directly.

You can check the ProjectName.AssemblyInfo.cs in obj folder (obj\Debug\netstandard2.0) has been updated by adding InternalsVisibleTo.

Feiyu Zhou
  • 4,344
  • 32
  • 36
0

In my case (.NET5 app) I needed both the csproj entry:

<ItemGroup>
    <AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
        <_Parameter1>MyAppName.UnitTests</_Parameter1>
    </AssemblyAttribute>
</ItemGroup>

and the internalvisibleto attribute to my class for this to work:

[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")] 
namespace MyAppName.Api.Something...
majjam
  • 1,286
  • 2
  • 15
  • 32