Click here to Skip to main content
15,885,366 members
Articles / DevOps / Unit Testing

Testing Private Methods with Visual Studio

Rate me:
Please Sign up or sign in to vote.
4.40/5 (5 votes)
3 Dec 2012CPOL2 min read 40.9K   61   11   9
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:

  1. You can test private methods using Reflection, 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 a private method to the DummyClass:
    C#
    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:

    C#
    [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.

  2. Another option is to use PrivateObject class. Using it, you can easily call private 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 using PrivateObject:
    C#
    [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);
    }
  3. 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:

    C#
    /// <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:

    C#
    /// <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 DummyClass_Accessor class with public methods only.

    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 add InternalsVisibleTo attribute to specify which assembly will see the internal methods, in our case:
    C#
    [assembly: InternalsVisibleTo("ConsoleLogger.Tests")]

    Now, we will add an internal method to the same class:

    C#
    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:

    C#
    [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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Telerik
Bulgaria Bulgaria
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionWhat about static method Pin
Tbs.User20-Jan-18 0:00
professionalTbs.User20-Jan-18 0:00 
GeneralMissing the point Pin
John Brett2-Dec-12 22:09
John Brett2-Dec-12 22:09 
GeneralRe: Missing the point Pin
springy764-Dec-12 0:23
springy764-Dec-12 0:23 
GeneralRe: Missing the point Pin
John Brett4-Dec-12 0:32
John Brett4-Dec-12 0:32 
GeneralRe: Missing the point Pin
springy764-Dec-12 0:56
springy764-Dec-12 0:56 
GeneralRe: Missing the point Pin
John Brett4-Dec-12 1:11
John Brett4-Dec-12 1:11 
GeneralMy vote of 3 Pin
Violet Tape30-Nov-12 8:53
Violet Tape30-Nov-12 8:53 
GeneralRe: My vote of 3 Pin
Vasil Trifonov2-Dec-12 21:22
Vasil Trifonov2-Dec-12 21:22 
GeneralRe: My vote of 3 Pin
Pete O'Hanlon3-Dec-12 22:20
mvePete O'Hanlon3-Dec-12 22:20 
It's not. It's really not a good idea to test the private ones. If the test fails because of a broken test, you attach the debugger to the public method and you identify which one failed by stepping through.

*pre-emptive celebratory nipple tassle jiggle* - Sean Ewington

"Mind bleach! Send me mind bleach!" - Nagy Vilmos


CodeStash - Online Snippet Management | My blog | MoXAML PowerToys | Mole 2010 - debugging made easier

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.