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.