Unit test a public method which relies on a protected method

Sometimes you need to write unit tests for a public method which is relying on some other protected methods. For example, lets say we have a business logic class which has a public GetGreatestId method, which returns the greatest Id from a list of Ids which is provided by a protected virtual GetData method:

public class MyBusiness
{
    public int GetGreatestId()
    {
        return GetData().Max();
    }
    protected virtual List<int> GetData()
    {
        //returns a list of integers from somewhere,
       Random r = new Random(); 
        return Enumerable.Range(1, 10).Select(x => r.Next(1000)).ToList();
    }
}

Later in this post we will talk about cases that the GetData method is private or it’s not virtual. For Now, let’s focus on protected virtual GetData method.

The question is how to write unit tests for GetGreatestId method? The main point for writing unit tests, is you need to have an expected list of data returned by GetData to be able to evaluate the result of GetGreatestId. To do so, You can use either of following solutions:

  • Usnig Inheritance
  • Using Mock Frameworks

Using Inheritance

You can derive a new class from MyBusiness and override GetData to return an expected list as result and write unit tests base on that:

private class MockedMyBusiness: MyBusiness
{
    protected override List<int> GetData()
    {
        return new List<int>() { 1, 2, 3, 4, 5, 6 };
    }
}

Then we can write unit test using the derived class:

[TestMethod]
public void TestMethod2()
{
    var business = new MockedMyBusiness();
    Assert.AreEqual(6, business.GetGreatestId());
}

Using Mock Frameworks

You can use mock frameworks like Mock to mock GetData to return an expected list as result and write unit tests base on that:

//using Moq;
//using Moq.Protected;

[TestMethod]
public void TestMethod1()
{
    var mock = new Mock<MyBusiness>();
    mock.Protected()
            .Setup<List<int>>("GetData")
            .Returns(new List<int> { 1, 2, 3, 4, 5, 6 });

    var business = mock.Object;
    Assert.AreEqual(6, business.GetGreatestId());
}

What should we do if the method is relied on some private or non virtual protected methods?

If you find out you can not write unit test for a public method because it relies on a private or a non virtual protected method, it means there is a design problem with the class which you are going to write unit test for that and it usually means this method belongs to another class or in fact the behavior of that method should be inject-able in the class.

For example, lets say we have a class like following, and we are going to make it unit testable:

public class MyBusiness
{
    public int GetGreatestId()
    {
        return GetData().Max();
    }
    private List<int> GetData()
    {
        Random r = new Random();
        return Enumerable.Range(1, 10).Select(x => r.Next(1000)).ToList();
    }
}

The fastest workaround is making the GetData method protected virtual and use one of the solutions which mentioned above.

But as a better fix, to make it unit testable, we should be able to inject GetData behavior to MyBusiness. To do so, we can create an interface IMyRepository having List<int> GetData(); method, and by changing constructor of MyBusiness class to public MyBusiness(IMyRepository myRepository) allow injecting IMyRepository to the business logic class.

By applying these changes our class will have a better design an will be more unit testable and extensible. Then to write unit tests, you can simply inject a known repository which returns an expected set of data and write unit tests based on that.

Here is the redesigned MyBusiness class:

public interface IMyRepository
{
    List<int> GetData();
}
public class MyRepository : IMyRepository
{
    public List<int> GetData()
    {
        Random r = new Random();
        return Enumerable.Range(1, 10).Select(x => r.Next(1000)).ToList();
    }
}
public class MyBusiness
{
    IMyRepository repository;
    public MyBusiness(IMyRepository myRepository)
    {
        repository = myRepository;
    }
    public int GetGreatestId()
    {
        return repository.GetData().Max();
    }
}

Final Note

Write unit testable code. When you design a class, it’s really important to design it in a way that the class be testable.

Do not avoid unit tests. One of the benefits of writing tests is detecting design problems. The classes should be unit testable and if you find out the class is not testable usually you should look for a extracting some part of the code and injecting it as dependency.

Follow SOLID. Following SOLID principles will help you alot to design better classes.

You May Also Like

About the Author: Reza Aghaei

I’ve been a .NET developer since 2004. During these years, as a developer, technical lead and architect, I’ve helped organizations and development teams in design and development of different kind of applications including LOB applications, Web and Windows application frameworks and RAD tools. As a teacher and mentor, I’ve trained tens of developers in C#, ASP.NET MVC and Windows Forms. As an interviewer I’ve helped organizations to assess and hire tens of qualified developers. I really enjoy learning new things, problem solving, knowledge sharing and helping other developers. I'm usually active in .NET related tags in stackoverflow to answer community questions. I also share technical blog posts in my blog as well as sharing sample codes in GitHub.

Leave a Reply

Your email address will not be published. Required fields are marked *