I have a class in Java, which has a private method for computing some default value; and two constructors, one of which omits that value and used the private method for getting the default.
public class C {
private static HelperABC getDefaultABC() {
return something; // this is a very complicated code
}
public C() {
return C(getDefaultABC());
}
public C(HelperABC abc) {
_abc = abc;
}
}
Now, I'm trying to write a test for this class, and would like to test BOTH constructors; with the second constructor being passed the default value.
Now, if getDefaultABC()
was public, that would be trivial:
// We are inside class test_C
// Assume that test_obj_C() method correctly tests the object of class C
C obj1 = new C();
test_obj_C(obj1);
HelperABC abc = C.getDefaultABC();
C obj2 = new C(abc);
test_obj_C(obj2);
However, since getDefaultABC()
is private, I can not call it from the test class!!!.
So, I'm forced to write something as stupid as:
// Assume that test_obj_C() method correctly tests the object of class C
C obj1 = new C();
test_obj_C(obj1);
// here we will insert 20 lines of code
// that are fully copied and pasted from C.getDefaultABC()
// - and if we ever change that method, the test breaks.
// In the end we end up with "HelperABC abc" variable
C obj2 = new C(abc);
test_obj_C(obj2);
Is there any way to resolve this conundrum (ideally, by somehow marking C.getDefaultABC()
as private for everyone EXCEPT for class test_C) aside from simply changing the method from private to public?
There's no equivalent to friend in Java. (that link from T I above) The way I'd do this is to declare the method as package protected like so:
static HelperABC getDefaultABC()
What this does, is allow your test code to call getDefaultABC()
as long as the test class is in the same package as C
is. Of course, that will let any other class in the same package call it too, but I think you're going to have to live with this, it's the best you can do without using reflection. But if you use reflection, every other class could also use reflection to call getDefaultABC()
so it's almost as "vulnerable". I'd just make it package protected so at least it's more readable than using reflection.
I'm going to give you the benefit of the doubt and say you probably inherited this code and it isn't currently testable. What you need to do is whatever you can to get getDefaultABC()
into a testable state. The tests don't even have to be good, you just need some confidence. Once you get some confidence in your tests, you'll discover there is code in getDefaultABC()
that really has nothing to do with getting the default ABC. Lower level abstractions like connecting to a DB or whatever. You need to identify that code and extract it into its own class. Then, write better tests for the extracted classes.
If you refactor this code into a really clean state, you should be able to get rid of your bad tests and put the getDefaultABC()
back into its private
state. private
implies internal implementations. You shouldn't be testing internal implementations directly.
Not absolutely clean, but you can use Reflection to call private method in a test.
To me it seems better to use reflection because it's noninvasive, you don't have to change methods access modifiers just because of tests.
Something like this:
Method method = obj1.getClass().getMethod("getDefaultABC");
method.setAccessible(true);
HelperABC abc = (HelperABC) method.invoke(obj1);
Make the method protected
protected static HelperABC getDefaultABC() {
return something; // this is a very complicated code
}
then extend the test class:
public class testC extends C{
...
}
If you can allow package level access, by following the same package rule for test classes where SUT and test class are on same package (different source folders), you can make it more clean IMHO.