Unit Testing and Private Methods

So you’re a TDD developer.  You code along merrily, writing failing unit tests, implementing them to a point of success, and then you refactor. But then, with no notice–WHAM–a private method. Now what!?

There are different schools of thought for this scenario. One camp says that if it’s not publicly exposed, you shouldn’t be testing it. I think that’s valid, but it’s equally valid to write test code for private methods. Just because a method’s private doesn’t mean it doesn’t deserve the same special attention. And just because I want to test a method that happens to be private doesn’t mean I want to expose it to the rest of the world! Let’s not worry about the debate for now. I’m going to show you how you can override private methods with Microsoft Fakes and how call private methods with Microsoft.VisualStudio.TestTools.UnitTesting.PrivateObject.

Override private methods

Problem: You have a private method with behavior that’s disruptive to unit testing. Regardless of the cause of the disruption, you want to provide an alternate implementation.

Solution: Use Microsoft Fakes to shim the private method. (Need to know more about Fakes? Check out my tutorial!)

Here’s how you can do it:

  1. Create an instance of your class
  2. Create a shim from  your class
  3. Override the private method on the shim
  4. Invoke the test method

Here’s a code example:

[TestMethod]
public void SecretMethod_FunctionalityIsOverridden()
{
    using (ShimsContext.Create())
    {
        var target = new FileReader("foo");
        var shim = new ShimFileReader(target);

        bool calledShimMethod = false;
        shim.SecretMethod = () => 
        {
            calledShimMethod = true;
            return "bar";
        };

        target.Read();

        Assert.IsTrue(calledShimMethod);
    }
}

Call private methods

Problem: You want to test a private method, but you can’t because it’s private.

Solution: Use Microsoft Test Framework’s included PrivateObject class.

The only thing I don’t like about PrivateObject is that it (presumably) uses reflection for method names, which means method names will be coded as strings with no compile-time checks. It’s very easy to use, though, and if you need to invoke a private method, I haven’t seen a better way to do it!

Here are the steps:

  1. Create an instance of your class
  2. Create a PrivateObject from your class
  3. Use the PrivateObject‘s Invoke method

And here’s the code:

[TestMethod]
public void SecretMethod_ReturnsHumorousMessage()
{
    var target = new FileReader("foo");
    var privateObject = new PrivateObject(target);
    var actual = privateObject.Invoke("SecretMethod");

    Assert.IsNotNull(actual);
}

Putting it all together (useless, but fun!)

I was able to verify both of these capabilities by writing a single unit test that overrides the private method, then calls the private method. Check it out:

[TestMethod]
public void SecretMethod_FunctionalityIsOverridden()
{
    using (ShimsContext.Create())
    {
        var target = new FileReader("foo");
        var shim = new ShimFileReader(target);

        // override the private method
        bool calledShimMethod = false;
        shim.SecretMethod = () => 
        {
            calledShimMethod = true;
            return "bar";
        };

        // call the private method
        var privateObject = new PrivateObject(target);
        var actual = privateObject.Invoke("SecretMethod");

        // assert that calling the private method
        // used the overridden implementation
        Assert.IsTrue(calledShimMethod);
    }
}
Advertisement

Author: Adam Prescott

I'm enthusiastic and passionate about creating intuitive, great-looking software. I strive to find the simplest solutions to complex problems, and I embrace agile principles and test-driven development.

6 thoughts on “Unit Testing and Private Methods”

  1. Hi Adam, override private method not work.
    The private method not exist in shim class.
    Are you sure about this code ?

  2. Hello,

    “Override private methods” is not working for me either. Tried to perform an easy test with a dummy class. The test method runs, but doesn’t bypass the original implementation and throws an exception (the one I left on purpose).

    Please note the code for better understanding:

    public class DummyClass
    {
    public int PublicMethod()
    {
    var i = PrivateMethod();
    return 1;
    }

    private int PrivateMethod()
    {
    throw new NotImplementedException();
    }
    }

    //Unit Test Class in separate project

    public class DummyClassTest
    {
    [TestMethod]
    public void PublicMethod_BypassPrivateMethod()
    {
    using (ShimsContext.Create())
    {
    // Arrange
    var called = false;

    var shimDummyClass = new ShimDummyClass();
    shimDummyClass.PrivateMethod = () =>
    {
    called = true;
    return 100;
    };

    var sut = new DummyClass();

    // Act
    sut.PublicMethod();

    // Assert
    Assert.IsTrue(called);

    }
    }
    }

Leave a comment

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: