Should I test that?

Should I test protected methods?

Verdict

No

Do not write tests that call protected methods directly; test their behaviour through the public methods of the base class and its subclasses.

Why

Do not test protected methods directly; test them through the public methods that call them. The typical case is a protected helper in a base class of your own application, called by two or three subclasses. Blast radius is users because customers see the wrong output, and Change frequency is rarely because a base class changes a few times a year. Detectability is immediately and Reversibility is trivial, because the tests of the public callers fail in CI and the change never merges. Test cost is moderate: a direct test needs a test subclass and breaks whenever the helper moves in the hierarchy, and rule R8 gives Do not test.

When the decision changes
WhenDecisionWhy
The subclass tests replace the protected method with a stub, so no test runs its codeTest minimally: one test of a public method that runs through the protected methodDetectability moves to same-day; Test cost falls to trivial for one public test
The base class ships in a library that other teams subclassTest each protected method that subclasses call, directly through a test subclassDetectability moves to eventually, Reversibility to with-effort, Test cost to trivial
The protected method calculates fees that customers are chargedTest mandatory, through the public methods, with boundary amountsBlast radius rises to money and Reversibility to costly
The protected method checks whether the current user may read a recordTest mandatory, through the public methods, including the denied casesBlast radius rises to safety-or-legal
A Java test in the same package can call the protected method without reflectionDo not test the protected method directly; keep testing its public callersTest cost falls to trivial, but Detectability stays immediately and Reversibility stays trivial

What breaks if you don't test

A broken helper fails the public tests that run through it, so a direct test adds nothing there. The risk sits in subclasses whose tests stub the helper. Take a base Exporter with a protected csvEscape() that the order and customer exporters call. A change that stops quoting commas splits "Smith, John" into two columns. If no subclass test uses a comma, the build stays green until a customer opens the file and reports it.

What you lose if you over-test

A direct test pins where code sits in the hierarchy, not what the class does. Move the helper into its own class or down into the one subclass that uses it, and the test fails although no customer sees a change. In C# a protected member is reachable only from derived classes, so each tested helper needs a wrapper subclass that exposes it as public, and every wrapper is a file to maintain.

What to do instead

Write unit tests at the level of the public methods. Pick inputs to each subclass's public method that drive every branch of the helper, and assert on the returned value or the saved record. For a protected hook that the base class calls, test the base class through one small test subclass, and each real override through its own subclass. If no public input reaches a branch, delete it or move it into a class with a public method. The JUnit user guide covers nested test classes, and the C# reference for protected lists who can reach a protected member.

When the answer changes

  • Other teams subclass the base class from a library that you publish.
  • The protected method calculates an amount that customers pay.
  • The subclass tests stub the protected method instead of running it.

Counterexample + Code example

The library where protected was the public API

The advice to test through public methods fails when the base class ships in a library. Other teams write the subclasses, so your CI never runs the public callers of the protected helper. A broken csvEscape() reaches them after an upgrade, and they wait for your patch release. Detectability moves to eventually and Reversibility to with-effort. Test cost falls to trivial, because a published signature stays fixed until a major version and its test seldom breaks. The framework gives Test. In Java, code in the same package can call a protected method, so the test needs no reflection:

package com.acme.export;

class ExporterTest {
    // Same package as Exporter, so the test may call the protected method.
    static class Probe extends Exporter {
        @Override
        protected List<List<String>> rows() { return List.of(); }
    }

    @Test
    void csvEscapeQuotesFieldsWithCommasAndQuotes() {
        assertEquals("\"Smith, \"\"Jr\"\"\"", new Probe().csvEscape("Smith, \"Jr\""));
    }
}

The Probe subclass has no logic, so the test fails only when csvEscape() changes its output.

FAQ

Should you unit test protected methods?

No, unit test protected methods through the public methods that call them. Test a protected method directly only when it belongs to a library that other teams subclass, because their code is then its only public caller.

How do I test a protected method in Java?

Put the test in the same package as the class, because Java gives package access to protected members, and call the method on a small test subclass. In application code, test the public method that calls it instead.

How do I test a protected method in C#?

Create a subclass in the test project that wraps the protected method in a public one, and call the wrapper. C# allows access to a plain protected member only from its own class and derived classes.

Should I make a private method protected to test it?

No, widening a private method to protected lets every subclass call and override it, and the test still depends on the class structure. Test the private method through its public callers, or move it into its own class with a public method.