Testing Private Methods with Visual Studio






4.40/5 (5 votes)
When writing unit tests in most of the cases, we have to test private methods. Let's see how this can be achieved using Visual Studio.
Introduction
When writing unit tests, in most of the cases we have to test private
methods. Let's see how this can be achieved using Visual Studio. I will give you three options:
- You can test
private
methods usingReflection
, but this is not always a good option, as when you change the name or input arguments of a method, you won't get any error when building, but you will get exceptions run time. We will use the project from my previous post Getting console output within a unit test. We will add aprivate
method to theDummyClass
:using System; namespace ConsoleLogger { public class DummyClass { public void WriteToConsole(string text) { Console.Write(text); } private void PrivateWriteToConsole(string text) { Console.Write("Private: " + text); } } }
Now we can add a unit test to test the method using reflection:
[TestMethod] public void PrivateWriteToConsoleReflection() { var currentConsoleOut = Console.Out; DummyClass target = new DummyClass(); Type type = typeof(DummyClass); string text = "Hello"; using (var consoleOutput = new ConsoleOutput()) { var method = type.GetMethod("PrivateWriteToConsole", BindingFlags.NonPublic | BindingFlags.Instance); method.Invoke(target, new object[1] { text }); Assert.AreEqual(string.Format("Private: {0}", text), consoleOutput.GetOuput()); } Assert.AreEqual(currentConsoleOut, Console.Out); }
We get and invoke the method using reflection.
- Another option is to use
PrivateObject
class. Using it, you can easily callprivate
method, but you have the same problem, you won't get compile exception when name or parameters are changed. Here is the same test written usingPrivateObject
:[TestMethod] public void PrivateWriteToConsolePrivateObject() { var currentConsoleOut = Console.Out; PrivateObject target = new PrivateObject(typeof(DummyClass)); string text = "Hello"; using (var consoleOutput = new ConsoleOutput()) { target.Invoke("PrivateWriteToConsole", text); Assert.AreEqual(string.Format("Private: {0}", text), consoleOutput.GetOuput()); } Assert.AreEqual(currentConsoleOut, Console.Out); }
- And here is the third option which I think is the best. You can add .accessor file containing the name of the assembly whose
private
methods you want to see, or you can use Visual Studio "Create Unit Tests..." wizard to do this for you:This will add the following unit test:
/// <summary> ///A test for PrivateWriteToConsole ///</summary> [TestMethod] [DeploymentItem("ConsoleLogger.exe")] public void PrivateWriteToConsoleTest() { DummyClass_Accessor target = new DummyClass_Accessor(); // TODO: Initialize to an appropriate value string text = string.Empty; // TODO: Initialize to an appropriate value target.PrivateWriteToConsole(text); Assert.Inconclusive("A method that does not return a value cannot be verified."); }
We will modify it for our needs:
/// <summary> ///A test for PrivateWriteToConsole ///</summary> [TestMethod] [DeploymentItem("ConsoleLogger.exe")] public void PrivateWriteToConsoleTest() { var currentConsoleOut = Console.Out; DummyClass_Accessor target = new DummyClass_Accessor(); string text = "Hello"; using (var consoleOutput = new ConsoleOutput()) { target.PrivateWriteToConsole(text); Assert.AreEqual(string.Format("Private: {0}", text), consoleOutput.GetOuput()); } Assert.AreEqual(currentConsoleOut, Console.Out); }
When Visual Studio builds the project, it will generate ConsoleLogger_Accessor.exe assembly, containing
Just to mention that you can test internal methods using the same approaches, but you will have one more option - in the AssemblyInfo file of the assembly being tested, you can addDummyClass_Accessor
class withpublic
methods only.InternalsVisibleTo
attribute to specify which assembly will see the internal methods, in our case:[assembly: InternalsVisibleTo("ConsoleLogger.Tests")]
Now, we will add an internal method to the same class:
using System; namespace ConsoleLogger { public class DummyClass { public void WriteToConsole(string text) { Console.Write(text); } private void PrivateWriteToConsole(string text) { Console.Write("Private: " + text); } internal void InternalWriteToConsole(string text) { Console.Write("Internal: " + text); } } }
and here is the working test method:
[TestMethod] public void InternalWriteToConsoleTest() { var currentConsoleOut = Console.Out; DummyClass target = new DummyClass(); string text = "Hello"; using (var consoleOutput = new ConsoleOutput()) { target.InternalWriteToConsole(text); Assert.AreEqual(string.Format("Internal: {0}", text), consoleOutput.GetOuput()); } Assert.AreEqual(currentConsoleOut, Console.Out); }
The entire code can be found here.