Last time, I wrote a follow-up to this article about whether or not you should unit test an abstract class. Now, I’d like to write a small follow-up to that follow-up 😊

To recap, here are the main points from the article and the previous follow-up:

  • You shouldn’t unit test abstract classes directly because they are implementation details.

  • Test them indirectly, via the concrete classes that inherit from the abstract class. That’s because tests should view domain classes as a black box. From the perspective of a unit test, it doesn’t matter whether those classes share their logic or duplicate it. What matters is the observable behavior.

  • If you write a library and the abstract class is meant for the external consumption, create a derived class yourself and test that class’s behavior.

  • These points are about abstract classes specifically. If you have a non-abstract base class, it’s fine to unit test it directly.

Someone also proposed to create an abstract base unit test class and have concrete test classes inherit from it.

So, assuming we have the following class hierarchy:

public abstract class Person
{
    public string Name { get; set; }

    public string GetSignature()
    {
        return $"Regards,\r\n{Name}";
    }
}

public class Student : Person
{
    /* Other methods */
}

public class Professor : Person
{
    /* Other methods */
}

we could create an abstract PersonTests class:

public abstract class PersonTests
{
    [Fact]
    public void Every_person_has_a_signature()
    {
        // Arrange
        Person person = CreatePerson("Name");

        // Act
        string personName = person.Name;

        // Assert
        Assert.Equal("Name", personName);
    }

    protected abstract Person CreatePerson(string name);
}

and then the StudentTests and ProfessorTests would both run this test against their own version of Person:

public class StudentTests : PersonTests
{
    protected override Person CreatePerson(string name)
    {
        return new Student(name);
    }

    /* Student-specific tests */
}

public class ProfessorTests : PersonTests
{
    protected override Person CreatePerson(string name)
    {
        return new Professor(name);
    }

    /* Professor-specific tests */
}

Such an implementation looks appealing because it allows us to avoid test duplication. All subclasses of Person essentially get the Every_person_has_a_signature test for free.

And still, I don’t recommend that you do that. This approach violates one of the principles I brought up above: that tests should view domain classes as a black box.

The introduction of PersonTests leaves too much knowledge of the production code’s internal structure. At the same time, you don’t need to duplicate the test either. A pragmatic approach here would be to add this test once to either StudentTests or ProfessorTests.

This solution is not ideal, of course, but it gives a better trade-off than coupling your tests to the production code’s implementation details (the base abstract class, that is).

--Vlad


Enjoy this message? Here are more things you might like:

Consulting and training — I offer consulting and group training for organizations on Unit Testing and Domain-Driven Design. Reply to this email with the word "consult" to discuss.

Unit Testing Principles, Patterns and Practices — A book for people who already have some experience with unit testing and want to bring their skills to the next level.
Learn more »

My Pluralsight courses — The topics include Unit Testing, Domain-Driven Design, and more.
Learn more »