Unit tests and protected methods

protected-padlockWhen working with Object Oriented Programming (OOP) languages we have the possibility to design our code and our classes using encapsulation. C# defines the “protected” accessibility level, which is accessible only from the containing class and from the types that inherits from this class. It is very helpful when you follow the Open-Closed Principle (OCP).

On the other side you might ask yourself how can we test the behavior of the methods if they are protected. And how to do so without breaking the encapsulation of the class.

Testing a protected method

I created a small custom class to expose my way of doing unit tests on protected methods, here is a class to test:

public class MyClass
{
    public int Counter { get; private set; }

    protected void IncrementCounter()
    {
        Counter++;
    }
}

The easiest thing I could do to be able to test my method is to replace the “protected” keyword by “public”. But I will not do that because, in my opinion when you write tests after the production code you should try to avoid updating the code as much as possible even to test it.

One other possibility is to make the method “internal”, this keyword specifies that the method can only be access from the current assembly. And then, for your tests project you can make them visible using the following attribute in the Properties/AssemblyInfo.cs file of your production project:

[assembly: InternalsVisibleTo("MyTestProject")]

This option is helpful when you want to limit the scope of an API defined in an assembly but without losing the ability to add unit tests on the entire project.

But again I will not use this trick to test my method, instead I will expose it using inheritance for my tests by creating a new class, only for testing purposes.

public class MyTestClass : MyClass
{
    public void ExposeIncrementCounter()
    {
         base.IncrementCounter();
    }
}

With this new class I can now test the behavior of the protected method without modifying it. And here is my test:

[TestMethod]
public void Test_MyClass()
{
    var myClass = new MyTestClass();
    myClass.ExposeIncrementCounter();
    myClass.Counter.ShouldBe(1);
}

Note that, once again, I use the Shouldly library to make my assertion.

Testing a call to a protected method

The other day one of my coworker was asking how can he test that a protected method is called when testing a public method. This is a tricky question and it depends on the context of your code. In his case the given method was not part of our project and therefore impossible to modify. He was still able to test the behavior of the method by mocking another class used by this protected method.

I think this experience is interesting because it shows that you can still write automated tests by going one level deeper. You might ask yourself if this is still a unit test. Maybe, maybe not but it is a test for your project.

And this story made me think: “How to test the call to a protected method?”. I created the following class to show you a way to be able to do so.

public class MySecondClass
{
    protected void InnerMethod()
    {
        //a lot of logic and dependencies
    }

    public void DoWork(bool doWork)
    {
        if (doWork)
        {
            InnerMethod();
        }
    }
}

In my case I want to test that, depending on the parameter’s value, the DoWork method calls the InnerMethod method. But without executing the code of the latter because it does too many things and therefore the test will be too long (if you have a similar situation you might have some code smells to fix).

To write my tests, this time I will slightly update the code to be able to do what I want. I am not a big fan of this but I don’t really see another option that will not imply more refactoring.

protected virtual void InnerMethod()
{
    //a lot of logic and dependencies
}

I simply made the method virtual in order to be able to override it. I did not really break the encapsulation, I just gave me more options. Now I can create a new class for testing purposes:

public class MySecondTestClass : MySecondClass
{
    public bool InnerMethodHasBeenCalled { get; private set; }

    protected override void InnerMethod()
    {
        InnerMethodHasBeenCalled = true;
    }
}

This class inherits from the class I want to test and does not modify its behavior regarding the DoWork method. It is a test double using the spy technique. I can now write the two following tests to check the behavior of the code.

[TestMethod]
public void Test_MySecondClass_False()
{
    var myClass = new MySecondTestClass();
    myClass.DoWork(false);
    myClass.IncrementCounterHasBeenCalled.ShouldBe(false);
}

[TestMethod]
public void Test_MySecondClass_True()
{
    var myClass = new MySecondTestClass();
    myClass.DoWork(true);
    myClass.IncrementCounterHasBeenCalled.ShouldBe(true);
}

If you write tests after writing the production code it is still possible to do so without changing too much of the code even with protected methods. If the code has been written using encapsulation there must be a reason for that and therefore the tests should not force you to break everything down.

What about private methods?

Now you might ask yourself, how to do this with private methods. With these kind of methods we cannot use inheritance for tests, so how?

Well you can use Microsoft Fakes to mock the calls to private methods, but in my opinion this is definitely overkill. Our projects should be testable without tools like this.

The other solution is to change the accessibility of the method to make it testable, because you believe that “Tests trump Encapsulation“.

But first, I think that when you want to test a private method, you should ask yourself some questions first. Why is this method private? Does it really needs to be private? Can I extract the logic inside into another class I will be able to test? Can I include its logic in the test of another method?

Using encapsulation should mean something from a design point of view. Writing tests after the code is better than writing no test at all. It also make you ask questions about the code you wrote: “What do I want to test?”.

And I think this is why we saw the arrival of practice such as Test Driven Development (TDD) to force us to ask these questions first in order to help us with the design and encapsulation of the production code. I will not enter in the debate of whether should you practice TDD or not. Or does TDD lead to good design or not. I don’t know enough to answer these questions and I especially think that it depends on the developer.

In this blog post I wanted to show you how to test protected methods without having too much impacts on the code, I hope it will help you. And always ask yourself about the Why when it comes to tests and only after the How.

See you next time!

4 thoughts on “Unit tests and protected methods

    • Hello Ninjatester, thank you for sharing this.

      Yes, using reflection is a good way to call private methods in order to test them. In my opinion when it comes to private methods is to test them through public methods, if the code is designed this way there must be a reason. The code is a part of something bigger and maybe it does not make sense to test it alone.

      Of course each case is specific and may require a different solution.

      Like

  1. A practice that I’ve done in the past is to put
    [assembly: InternalsVisibleTo(“MyTestProject”)] in a new file called something like TestInternals.cs

    The idea is to make these settings more explicit. Most developers don’t think to look in AssemblyInfo.cs

    Some prefer this and others don’t. It’s a extra file that might not be needed. I think it’s important for the team to have a coding standard on which way to go.

    Like

    • Hello Kevin, thank you for the tip, it seems very helpful, I did not think of that.

      And I also agree with you regarding the coding standards share within a team, it saves you a lot of troubles and misunderstandings.

      Like

Leave a Reply

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 )

Twitter picture

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

Facebook photo

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

Google+ photo

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

Connecting to %s