2

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?

DVK
  • 126,886
  • 32
  • 213
  • 327
  • 1
    private static? why!, has no sense – nachokk Jun 26 '13 at 19:48
  • i think [this](http://stackoverflow.com/a/316838/1114171) answer should help – T I Jun 26 '13 at 19:49
  • 4
    If the method is private, it is only ever going be called internally. [Test the method(s) that call it](http://programmers.stackexchange.com/questions/100959/how-do-you-unit-test-private-methods). If you insist on testing it separately, you can use reflection as outlined [here](http://stackoverflow.com/questions/34571/whats-the-proper-way-to-test-a-class-with-private-methods-using-junit). – DannyMo Jun 26 '13 at 19:49
  • @nachokk - it has to be static because it is called from other static methods (specifically, a singleton). – DVK Jun 26 '13 at 19:49
  • 1
    You can use reflection. – Simon Arsenault Jun 26 '13 at 19:50
  • 2
    Many testing frameworks allow you to handle this and test private methods. That said, normal convention is not to test private methods, but rather the methods that call the private ones. (That's ignoring that `private static` doesn't make a lot of sense since in order to call it you'd need to be *in an instance of that object*) – Brian Roach Jun 26 '13 at 19:51
  • If that method needs to be executed from another class, then it shouldn't be private. You need to restructure/refactor your code. – BLuFeNiX Jun 26 '13 at 19:55
  • @BrianRoach - as I tried to express (probably not clearly enough) in the question, the purpose of calling the static method from the test is NOT to test it, but to obtain a return value to be used as a parameter in a test; where computing that value would be cumbersome. (it's not so much the fact that it's THE default value, it's that I would have to write pretty much the same 20 lines of code used to compute the default value to compute ANY value to pass in) – DVK Jun 26 '13 at 19:59
  • @BLuFeNiX - it does not need to be executed from a random "another" class, but very explicitly from a class designed to test the main class. Unfortunately, our convention - which I can't break - does not allow them to be in the same package - one is package C, one is package C.test. – DVK Jun 26 '13 at 20:01
  • @DVK - ok. Answer is still the same though; you'd need to use reflection (private never *really* means private ;) ). Some of the testing frameworks give you helper classes/methods to facilitate that but you could just do it yourself as well. – Brian Roach Jun 26 '13 at 20:03
  • @BrianRoach - this junker doesn't even use JUnit... WHAT frameworks? :) Thanks; I will try reflection - see my comment on Ondrej's answer re: security – DVK Jun 26 '13 at 20:20
  • @damo - that's exactly what I'm doing. I'm testing 2 constructors. I am NOT testing that method. – DVK Jun 26 '13 at 20:21

4 Answers4

4

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.

Community
  • 1
  • 1
Daniel Kaplan
  • 62,768
  • 50
  • 234
  • 356
  • Correct. I have an inherited code with an established APIs, and no decent unit test coverage (make that "no unit test coverage"). I'm making a small tweak, but need to add a full unit test - and I'm not a Java expert by any stretch of imagination. – DVK Jun 26 '13 at 20:03
  • @DVK does my answer help? What's it missing? – Daniel Kaplan Jun 26 '13 at 20:04
  • it's a definite +1, but does not help. First half would be ideal, but sadly doesn't help for the stupid reason that our coding conventions force the class and the test class to be in 2 different packages (say, `C` and `C.test`). – DVK Jun 26 '13 at 20:05
  • Second half doesn't help because the goal of the exercise is NOT testing of the private method, but the testing of a **second constructor that doesn't call it**; the private method is merely a good way for me to get an argument to pass to the constructor without cloning the code in the method into the test. – DVK Jun 26 '13 at 20:07
  • @DVK: And that can't change even if it makes your code better? Why don't you take *all* the code in that method, put it in its own class, then have your test use the new class? – Daniel Kaplan Jun 26 '13 at 20:09
  • the method would still be private in the new class, no? (if it should have been public, I can simply make it public in the original class :) – DVK Jun 26 '13 at 20:17
  • Not necessarily. You could make a new `ABCBuilder` class that `C` uses in the `getDefaultABC()` method. eg: `new ABCBuilder().build()`. This `build()` method will have all the logic `getDefaultABC()` had in it. Make that method public, now anyone can call it. – Daniel Kaplan Jun 26 '13 at 20:21
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/32443/discussion-between-dvk-and-tietyt) – DVK Jun 26 '13 at 20:23
2

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);
Ondrej Bozek
  • 10,987
  • 7
  • 54
  • 70
  • I was reading about reflection on some of the links posted in comments and while sounding interesting, there seems to be a feeling I get that this approach somehow violates security? Or is that irrelevant for unit tests? – DVK Jun 26 '13 at 20:08
  • Yes I think that in case of tests it shouldn't be a problem. You secured the method for normal use in your app, contrary you would like to overcome the security in Tests. – Ondrej Bozek Jun 26 '13 at 20:12
  • I think it's better then to change your method contract just because of a Test. – Ondrej Bozek Jun 26 '13 at 20:13
  • Changing method access modifiers violates security. – Ondrej Bozek Jun 26 '13 at 20:47
1

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{
   ...
}
mel3kings
  • 8,857
  • 3
  • 60
  • 68
0

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.

Ravi Vasamsetty
  • 403
  • 2
  • 6