Go to content Go to navigation Go to search

Brokenwire.NET::Programming

How to unittest internals
· 2010-12-08 16:13 by Thijs Kroesbergen for Brokenwire.NET

This how-to is written by Mark Groot, and published on here on Brokenwire.net with his permission.

Normally when you try to unit test a method from an internal class, u will receive a message saying something like ‘… is inaccessible due to its protectionlevel’. The typical response in these kind of situations is either ‘ah, f.. it. I’ll just skip this test’ or ‘I’ll just make the class public. Who cares.’.

What you actually want to do is to make the internal method accessible to your unit test project. As it turns out, that this isn’t really all that difficult. Visual Studio will do most work for you. There are however a few things u have to keep in mind. In this article I will stretch out on those things.

First, let’s make a little console app which contains an internal class. (The entire solution can be downloaded here).

public interface ICoinFlip
{
  bool YouWon(bool choiceIsHead);
}


internal class CoinFlip : ICoinFlip
{
  public CoinFlip()
  {
  }

  bool ICoinFlip.YouWon(bool choiceIsHead)
  {
      // You can only win if you pick head
      // This is stupid, but that is not the point here
      return choiceIsHead;
  }
}

static void Main(string[] args)
{
  ICoinFlip flip = new CoinFlip();
            
  bool coinSideIsHead = (new Random()).Next(0, 2) == 1;

  if (flip.YouWon(true) == coinSideIsHead)
    Console.WriteLine("You win!");
  else
    Console.WriteLine("You lose!");
}

When you run this code you will get a nice little console window telling u whether or not you won.

Now how to test this? There are a few steps you have to take.

Let Visual Studio create a unittest and a unit test project for you.

You can create a unit test for the YouWon method by right-clicking the method and selecting ‘Create Unit Tests…’ from the contextmenu.

Clicking the menu option will popup the following window.

In the treeview you can select additional methods for which you want to create unit tests. You have to select a project from the listview in which the test has to be created.

Now Visual Studio will create a test class for the selected method in your selected unit test project including all the necessary references.

Visual Studio will also add the assembly:InternalsVisibleTo attribute to the AssemblyInfo file of your console application.

[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("coinflipTest")]

By using this attribute the unit test assembly will become a “friend assembly ” of the main assembly, and therefore the main assembly will allow the test assembly access to its internals.

Edit the test method

Visual Studio will generate a TestMethod in the new test class looking like this:

After some clean up the code looks like this:

[TestMethod()]
[DeploymentItem("coinflip.exe")]
public void YouWonTest()
{
  ICoinFlip target = new CoinFlip();
  bool choiceIsHead = false;
  bool expected = false;
  bool actual;
  actual = target.YouWon(choiceIsHead);
  Assert.AreEqual(expected, actual);
}

We enter the value false into the method and we expect to receive a value false back. If that is true, then the test passed.

The following steps are only necessary when you have a strongly signed project:

Find the public key of your unit test project

There was a time where finding the public key token using sn –T was enough. Now however, the InternalsVisibleTo  attribute uses the public key.
We can retrieve that key in two steps by using the sn.exe tool.
First create a file with the public key from the assembly key file using the following command (inside a Visual Studio command prompt)

sn –p NameOfYourKeyFile.snk NameOfYourOutputFile.PublicKey.

Use the output file to retrieve the public key with the –tp option of sn.exe:

sn –tp NameOfYourOutputFile.PublicKey.

The result is some text and a large key looking like this:

Microsoft (R) .NET Framework Strong Name Utility  Version 4.0.30319.1
Copyright (c) Microsoft Corporation.  All rights reserved.

Public key is
00240000048000009400000006020000002400005253413100040000010001003b825be7fe7e93
f782f0ff49cb2c86845698b15301834dc63cf9b9c62ea95aa02151a069cafb82f1902dfec1e2be
16b7f05d99e84a5796060d044ab62a1bcc6ffc7d12ab03827c22b4a70a67797bc82aea51a6c9b9
c31fee99410dbbc54b32dd9b18f202e97912b967120d4655a8575a2568738e7a08b662a0004440
91d500cf

Public key token is de3fc1b9aa83af40

Then add the key into the AssemblyInfo of the console application.

As we saw before, when we created the unit test the following entry was created in the AssemblyInfo file:

[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("coinflipTest")]

You have to change this into:

[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("coinflipTest, PublicKey=PASTE-YOUR-PUBLIC-KEY-HERE")]

Here you copy-paste the public key from the previous step into the marked section. You will have to remove the linebreaks, but that should be rather obvious. Don’t copy the key shown here, you have to use your own key…

To see if this works: build the project, and run the test.

Enjoy!

Permalink -