1

I understand protected method is good for unit testing since you can easily mock by the class by overriding the protected method for the sake of testing.

However, protected variable is just a global variable (although limited) and should be avoided at all.

Especially if you've all the essential getter/setter declared, you should have zero protected variable and it should not affect anything else.

So, in the future, if I am going to elimiate all protected variables in my codes, are there any caveat that I am not aware of?

2

3 Answers 3

5

There is no need to test private or protected member variables, and getter/setter methods are almost always part of the public API, so I'm going to assume that you're talking about unit testing private or protected methods.

This is probably going to be an unpopular opinion, but here goes anyway.

Principle Number One: If your unit testing framework cannot test methods unless they are public, virtual or backed by an interface, your unit testing framework sucks and you need to find a new one.

Now that you have a testing framework that can test private methods...

Principle Number Two: You don't need to mock a private method. Ever.

Do you see why? Private methods are not part of the public API of your class, so mocking them doesn't make any sense.

I know there's a school of thought that says that you should make all methods public so that they can be easily unit tested, and that, if your method is not public, it doesn't need to be unit tested, because it gets touched by some public method anyway.

Hogwash.

First of all, why should I pollute my public API with methods that may never be called directly by the consumer?

Second, why shouldn't I be able to test my private methods, directly? The whole point of refactoring code into separate methods is to sequester functionality behind those methods. Methods are a little testable black box, and the need to create them and test them independently doesn't diminish merely because I have marked them private.

Don't even get me started on the "one interface for every class" practice.

11
  • +1, I guess your opinion is not so unpopular as you think.
    – Doc Brown
    Aug 12, 2014 at 16:15
  • 1
    The problem with testing private methods is that they're implementation details, so those tests will break when your implementation changes. Public API tests don't break unless you make backwards-incompatible changes (in which case updating the unit tests is the least of your worries). If there's such a big gap between the public API and the implementation details, that might be a sign you're doing too much in one class. I agree with pretty much everything else though.
    – Doval
    Aug 12, 2014 at 16:57
  • 3
    Yes, changing a public method to produce a different result will require changing the tests, but it also requires changing all the client code. My point is that if you write unit tests against private methods, implementation changes that are correct (i.e. don't change the outward behavior) will break your tests. That's unnecessary maintenance overhead. If there's something wrong with a private method, public methods that call it will give a wrong result. Finding the culprit isn't terribly difficult - you can just set a breakpoint on the public method and see where things go wrong.
    – Doval
    Aug 12, 2014 at 17:12
  • 2
    Agreed about not breaking encapsulation, but a separate collaborator object doesn't truly break it. You build complex stuff by composing simpler stuff. Simpler stuff can and should be tested separately whenever possible, in a "divide & conquer" way. Black box testing has its difficulties, but you gain having less brittle tests -- and fixing brittle tests is a tremendous time waster.
    – Andres F.
    Aug 12, 2014 at 17:37
  • 1
    Note that all relevant uses of innerStuff() are covered in unit tests for doSomething() :)
    – Andres F.
    Aug 12, 2014 at 17:38
2

You seem to have a misconception that is leading to your confusion.

However, protected variable is just a global variable (although limited) and should be avoided at all.

A protected field in a class limits its visibility to the class, other classes in the same package, and subclasses. This is not a global variable.

To have a 'global' variable in Java one would have something that looks roughly like:

class Foo {
    public static int bar;
}

The key points there is that there is only one Foo.bar in the code and it is accessible to everything. Changing this to protected static int bar will reduce its visibility, but it is still something that is a global shared state.

On the other hand a class that looks like:

class Foo {
    protected int bar;
}

This has a protected field that is only accessible to some things (as mentioned above) and is an instance variable - that there is one instance per object. It is not a global variable. There are good reasons to have code that looks like this (you will often find it in abstract classes).


Especially if you've all the essential getter/setter declared, you should have zero protected variable and it should not affect anything else.

Getters and setters are different than accessibility. The bean model for Java often dictates their existence. Typically, one has private properties rather than protected in this case.

Though I will point out again that a protected field is one of where it is visible.


In the context of testing...

One approach to making specific methods return a specific value is to extend the class and rewrite that method. This is only possible if the method is not final or private.

So, yes, you could indeed use that approach.

However, there is awkwardness here in that you need to make sure that your rewritten class is in test packages only to make sure it doesn't slip into your release code.

The other thing is that when you are doing this, you are very tempted to write additional logic in there that substantially differs from the extended class. This introduces the danger that you won't actually be testing what you think you are testing.

Both of these issues can be addressed by instead mocking the class in the test code and specifically returning the desired values. It makes it very clear what you are sticking in where with no additional logic and that the code doesn't get into production (mocked classes only exist at runtime in tests).

There are still gotchas to watch out for in here, because it still hasn't removed the logic (just ripped it out) and you still need to make sure that part works correctly.


Ahh, but "you can't mock or unit test private methods easily" people will point out. "If you can t do it easily, people will either not do it, or do it wrong." And that is true.

But there is another option. The default level protection within Java. See Controlling Access to Members of a Class

package com.se.prog.demo;

class Foo {
    public int bar() { return 42; }
    protected int baz() { return 4; }
    int qux() { return 7; }
    private int xyzzy() { return 13; }
}

The methods bar, baz and qux can all be mocked easily. qux can only be accessed by other classes in the same package as com.se.prog.demo -- and you control that list.

With the maven directory structure, one would have

src/main/java/com/se/prog/demo/Foo.java
   ^^^^^^

but there's also

src/test/java/com/se/prog/demo/TestFoo.java
   ^^^^^^

which can access qux() in Foo allowing for unit testing of the gutsier parts of Foo, without exposing it to the world (and Foo's subclasses).

In some circles, this is seen as a better approach for methods that are the innards of the class but still need to be tested or mocked than protected or private.

0

Protected variables exist for a reason: they give parent classes the ability to let their children know about the their internal representation. If your public get/set methods aren't just pass-through (maybe the get/set is typed as a DateTime, but internally you store everything as Unix time in an int field, for example), then the classes that derive from that class may need to access the underlying value in the field, and not what the object exposes to the outside world (via the get function).

2
  • 2
    Why is this important? It's unclear to me what point you're trying to make. Aug 12, 2014 at 15:54
  • 1
    Read his actual question. He wanted to know: So, in the future, if I am going to eliminate all protected variables in my codes, are there any caveat that I am not aware of? The caveat is he will not be able to handle the situations I described if he restricts himself to never using protected variables... he's coming at the problem from a TDD perspective and not a "possible architectural needs" perspective. Aug 12, 2014 at 16:16

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