Things have improved quite a bit since VSTS 2008 beta when I wrote a post on unit testing private methods.
It appears that both public and internal classes, and methods from public to private can be unit tested in a fairly straightforward manner.
To illustrate that, I coded a simple application and highlighted the unit testing of a private method.
Let’s say I want to create a simulation of the Monopoly game.
I create a new solution with two projects to begin with.
In a console application project the startup code could look like this:
const int numberOfPlayers = 3; var game = new MonopolyGame(numberOfPlayers); game.Play();
In a library project the code for the MonopolyGame class could look like this:
internal class MonopolyGame { private readonly int numberOfPlayers; private const int MinimumNumberOfPlayers = 2; private const int MaximumNumberOfPlayers = 8; private List<Player> players; public MonopolyGame(int numberOfPlayers) { this.numberOfPlayers = numberOfPlayers; } public void Play() { if (this.HasRequiredNumberOfPlayers()) { this.players.ForEach(player => player.TakeTurn()); } } protected bool HasRequiredNumberOfPlayers() { return numberOfPlayers >= MinimumNumberOfPlayers && numberOfPlayers <= MaximumNumberOfPlayers; } }
Then we add a Test project to the solution.
And add a unit test with the Add / Unit Tests… menu option. Not Add / New Test… which would not generate everything we need for us. (This is something to explore: how to do it all manually?)
This adds all the references and the test reference needed. It adds the unit test class and auto-generates some code for us.
This is what the code looks like for the HasRequiredNumberOfPlayers() test method:
/// <summary> ///A test for HasRequiredNumberOfPlayers ///</summary> [TestMethod()] [DeploymentItem("MonopolyGameBL.dll")] public void HasRequiredNumberOfPlayersTest() { PrivateObject param0 = null; // TODO: Initialize to an appropriate value MonopolyGame_Accessor target = new MonopolyGame_Accessor(param0); // TODO: Initialize to an appropriate value bool expected = false; // TODO: Initialize to an appropriate value bool actual; actual = target.HasRequiredNumberOfPlayers(); Assert.AreEqual(expected, actual); Assert.Inconclusive("Verify the correctness of this test method."); }
Initializing the way we need to, we get the following code:
/// <summary> ///A test for HasRequiredNumberOfPlayers ///</summary> [TestMethod()] [DeploymentItem("MonopolyGameBL.dll")] public void HasRequiredNumberOfPlayersTest() { int numberOfPlayers = 3; MonopolyGame_Accessor game = new MonopolyGame_Accessor(numberOfPlayers); bool expected = true; bool actual; actual = game.HasRequiredNumberOfPlayers(); Assert.AreEqual(expected, actual); }
This can be simplified a little bit:
/// <summary> ///A test for HasRequiredNumberOfPlayers ///</summary> [TestMethod()] [DeploymentItem("MonopolyGameBL.dll")] public void HasRequiredNumberOfPlayersTest() { int numberOfPlayers = 3; var game = new MonopolyGame_Accessor(numberOfPlayers); Assert.IsTrue(game.HasRequiredNumberOfPlayers()); }
Now, what do I do if I want to test another valid value and let’s say a few that are outside the valid range (at least one under and one above)?
I see three options.
Notes:
You can enable specific other assemblies to access your internal types by using the InternalsVisibleToAttribute. For more information, see Friend Assemblies (C# and Visual Basic).