Testing Protected Methods is Easy

We all know that you shouldn’t test private methods, but the same can’t be said about protected methods. Look at this slightly modified version of the UserBinder from the CodeBetter.Canvas

project:

public abstract class ModelBinder<T> : IModelBinder where T : class
{
   //..all types of code

    public object BindModel(NameValueCollection parameters, Action<string> addErrors)
    {       
        var id = Get("id").ToInt(0);
        T entity = null;
        if (id == 0)
        {
            entity = BindNew();
        }
        else
        {
            entity = _repository.Find<T>(id);
            BindExisting(entity);
        }
        if (entity == null) 
        {
            AddError("Something bad happened");
            return;
        }
        var errors = Validate(entity);
        if (errors != null)
        {
            errors.Each(addErrors);
        }
        return entity;
    }

    protected abstract T BindNew();
    protected abstract void BindExisting(T original);
    protected virtual string[] Validate(T entity)
    {
        return null;
    }    
}

We are binding a new or existing user to our submitted data (that’s what BindExisting and BindExisting do), and assuming that worked, calling Validate. Its up to each binder to implement the BindNew, BindExisting and Validate methods (the last one being optional). Our UserBinder looks something like:

public class UserBinder : ModelBinder<User>
{
    protected override User BindNew()
    {
        var user = new User();
        BindCommon(user);
        return user;
    }
    
    protected override void BindExisting(User user)
    {
        BindCommon(user);
    }
    
    private void BindCommon(User user)
    {
        //Parameters is defined in the base binder, it just wraps Request.Form
        user.Name = Parameters("Name"); 
        user.Email = Parameters("Email");
        return user;
    }
    
    protected override string[] Validate(User user)
    {
        if (user.Name == "Karl")
        {
            return new[] {"there can be only one"};
        }
        return null;
    }
}

So far everything’s great, our base ModelBinder class provides a generic binding framework, and subclasses specify the binding implementation for individual entities.

However, when we go to unit test our UserBinder‘s Validate method, we can’t access it because its marked as protected. A simple solution might be to change it to public, or protected internal (and use the ugly InternalsVisibleTo attribute). I’m a big believer that real code should never be modified for the purpose of unit testing (including marking methods as virtual (I believe in virtual by default, but not because of unit testing)). Frankly, if you are having to change production code to make it testable, then you are doing something wrong.

The right solution (or at least the solution I’ve been using), is to simply subclass the UserBinder within your test project:

public class TestUserBinder : UserBinder
{
    public string[] PublicValidate(User user)
    {
        //forward the call to the base implementation
        return Validate(user);
    }
}

We can now write a straightforward test:

[Fact]
public void ReturnsErrorWhenNameIsReserved()
{
    var binder = new TestUserBinder();
    var errors = binder.PublicValidate(new User{ Name = "Karl"});
    Assert.Equal(1, errors.Length);
    Assert.Equal("there can be only one", errors[0]);
}
This entry was posted in Uncategorized. Bookmark the permalink. Follow any comments here with the RSS feed for this post.

4 Responses to Testing Protected Methods is Easy

  1. Matt says:

    Karl,

    What framework is the [FACT] attribute from? I’m curious to know more about it.

  2. Haacked says:

    That pattern is known as “Test specific subclass” or “Subclass and override”. Yeah, I learned those pattern names because I received a lot of flack about it when I wrote about that very same technique. 😉

    http://haacked.com/archive/2007/12/06/test-specific-subclasses-vs-partial-mocks.aspx

    Honestly, I’m still of the mind that this pattern has its place (template method pattern etc…) I do think our efforts to avoid the need for test specific subclasses helped improve the design of ASP.NET MVC.

  3. Chris Falter says:

    “real code should never be modified for the purpose of unit testing”

    This is not always true; sometimes difficulties in testing code point to a flaw in its design. For example, if you need to test some validation logic, and its buried in the internals of a class or method, maybe we need to refactor.

    In this particular piece of code, I would consider favoring composition over inheritance. That is, let the validation logic live in a separate interface or abstract class, and let the ModelBinder class use an instance of it. There could be a UserValidation class, for example. To test it, mock the ModelBinder (which would also implement an interface) and use the mock in the tests for UserValidation.

    I haven’t given much attention to all the ways that the various types of ModelBinder might vary, so the itf/abstract class might include more than validation logic.

    That said, there are times when, due to widespread use in client code, I have not been able to refactor a class to favor composition over inheritance. And I have used exactly this approach in its unit tests.