5

I am curious if the following example of testing a class with protected methods is fine.

For example, say you have the following class Foo that has method a() of return type Bar:

class Foo {

    protected Bar a() { ... }
    
    // ... other stuff
}

And the following test class TestFoo (using JUnit):

// Question: is extending a source class a bad practice?
class TestFoo extends Foo {
    @BeforeAll
    public void setup() { /* some setup stuff */ }

    @Test
    public void testA() {
        Bar value = a();
        assertEquals(value, ...);
    }
}

Also, let's assume that I don't want to use libraries like PowerMockito (say, I don't want to deal with anything that uses Java Reflection API) and I want to keep it really simple. I know there has been a debate on whether to unit-test private methods or not and that can be applied to protected methods, but assuming you want to test this protected method, is this approach correct?

Or am I just crazy?

8
  • 1
    Have you tried this? I don't think it will compile, because a private method is not visible outside the class which declares it. TestFoo can't call Foo::a.
    – bdsl
    Commented Mar 10, 2021 at 18:52
  • @bdsl, thanks for the good catch. It should have been protected.
    – oneturkmen
    Commented Mar 10, 2021 at 18:55
  • Right, that makes sense.
    – bdsl
    Commented Mar 10, 2021 at 18:56
  • 1
    This slightly obfuscates the construction of Foo. If you just go new Foo(), no problem. But it there are multiple constructors with complex arguments, it adds work (Bar will need a lot of calls to super(x,y,z)) and may be slightly confusing. Also, many classes are final and cannot be extended. I personally think this is a dubious practice, for reasons just like this use case, but others differ.
    – user949300
    Commented Mar 10, 2021 at 19:24
  • 1
    Yes, its fine. Sometimes it is the best approach, in fact. But a limitation is that the class itself has to be ready for it - you have to be able to create a child class instance, for example, and maneuver it to the correct state. And to do that it should be with documented or at least well-understood methods of the class, otherwise your test will be fairly fragile.
    – davidbak
    Commented Mar 10, 2021 at 20:28

4 Answers 4

12

One of the dangers of testing is locking down an implementation. Tests should make it easier to refactor not harder. Tests that don’t focus on the public interface tend to lock down implementation.

Extending a class let’s you come in the back door and fiddle with non public stuff. That puts the focus of testing in the wrong place.

You may feel the private stuff needs testing. However, if you can’t achieve code coverage by testing the public interface then you have too much code. Get rid of the unreachable code. Maybe put it in a junk folder until you have a real use for it.

For example, you have a protected constructor. Fine. If it exists for a reason then something somewhere uses it when it’s accessed publicly. Test that.

6

Sometimes classes are designed such that they can only be used as a base-class but where you still want to test them independently of the existing derived classes.

In such cases, the usual pattern is to create a derived class specifically for testing the base class, which is independent of the class that houses the actual tests. That pattern can also be used to gain access to protected members for testing.

It would look like this

class FooForTest extends Foo {
  public Bar public_a() {
    return this.a();
  }
};

class TestFoo {
    @BeforeAll
    public void setup() { /* some setup stuff */ }

    @Test
    public void testA() {
        FooForTest foo = new FooForTest();
        Bar value = foo.public_a();
        assertEquals(value, ...);
    }
}
1
  • I've often thought about this as well, but I've never gone this route. I always felt it was more appropriate to test the base class via a concrete derived class used in production code. Still, +1. Just because I haven't found a use case for this yet doesn't mean I never will. Commented Mar 11, 2021 at 14:55
2

Also, let's assume that I don't want to use libraries like PowerMockito (say, I don't want to deal with anything that uses Java Reflection API) and I want to keep it really simple.

The simplest way - both from an implementation standpoint and from a readability standpoint - is to use the tailor-made tools provided to you rather than rolling your own for each thing you want to test. What you're doing is how some of these tools approach testing protected/private methods, but they'll do it in a more consistent way and without you having to maintain all of that scaffolding.

0

While I would not use this pattern for new code, it is a well known and helpful pattern for testing legacy code. It comes in two flavors:

  1. the extending class is a class that is only used in test
  2. the test class (fixture) is itself extending

#2 is called the Self ShuntPattern and was first described by Michael Feathers, IIRC.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.