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?

EricSchaefer
  • 2,111

4 Answers4

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.

candied_orange
  • 119,268
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, ...);
}

}

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.

Telastyn
  • 110,259
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.

EricSchaefer
  • 2,111