77

I have been assigned the task of unit testing a class that I never worked directly on with JUnit, and am strictly forbidden to change the code in the package. This is usually no issue, since most of our unit testing is just for functionality and input/output consistency, which can be done simply by running routines and checking their return values.

However, occasionally there is a need to check a private variable within the class, or directly edit a private variable to check some internal behavior. Is there a way to gain access to these, whether through JUnit or any other way, for the purpose of unit testing without actually changing any of the code in the original source package? And if not, how do programmers handle this issue in the real world where a unit tester may not be the same person as the coder?

donnyton
  • 5,874
  • 9
  • 42
  • 60
  • how about reflection? or adding getter and setter by code weaving within the test? – peshkira Jul 14 '11 at 14:27
  • 2
    Isn't the internal behavior used in the implementation of the public fields and methods? If not, what is its purpose? – Kwebble Jul 14 '11 at 14:46
  • i prefer to execute the flow that causes the private field to change its state. and then execute the new flow. imo, the private field exists to hold some previous behavior which in turn affects the next behavior. so unit-test is still behavior oriented and not implementation oriented, because you infer the behavior from the code which is common when you are not the owner. – Hanif Oct 03 '19 at 16:23
  • @Hanif, what if the private field is also modified only in a callback? How do you solve that. I am facing a similar issue where the private variable is set in a callback and I need the private variable to be set to be able to test some other methods. – Clement Osei Tano Nov 19 '20 at 13:09

6 Answers6

66

First of all, you are in a bad position now - having the task of writing tests for the code you did not originally create and without any changes - nightmare! Talk to your boss and explain, it is not possible to test the code without making it "testable". To make code testable you usually do some important changes;

Regarding private variables. You actually never should do that. Aiming to test private variables is the first sign that something wrong with the current design. Private variables are part of the implementation, tests should focus on behavior rather of implementation details.

Sometimes, private field are exposed to public access with some getter. I do that, but try to avoid as much as possible (mark in comments, like 'used for testing').

Since you have no possibility to change the code, I don't see possibility (I mean real possibility, not like Reflection hacks etc.) to check private variable.

Agricola
  • 572
  • 1
  • 8
  • 20
Alexander Beletsky
  • 19,453
  • 9
  • 63
  • 86
  • 3
    Having separate developers for code and tests could be an advantage since it requires 2 persons to interpret the specifications. If they differ in interpretation the requirements were not clear enough, the tests will probably fail and you've detected possible errors. Great! – Kwebble Jul 14 '11 at 14:44
  • 7
    @Kwebble my experience shows that separation "coders" & "testers" is a really bad practice. ) – Alexander Beletsky Jul 14 '11 at 14:53
  • Indeed, there are some design issues in the class (which is actually an implementation of a singleton) that we will need to address eventually. It's for reasons beyond my (and my boss's) control that I am not allowed to touch the code itself. This specific issue appeared because while there were ways to change a particular private field, all those routines would have caused huge side effects that we wanted to avoid. – donnyton Jul 14 '11 at 15:13
  • @alexanderb sure, it's possible that it doesn't fit in the development team or the process. And there are other ways of making sure the implementation fits the requirements, like code review or functional tests. But I think it might uncover errors earlier. – Kwebble Jul 14 '11 at 15:16
  • 1
    @donnyton: I hope you don't get a failed test, because that would require changes to the code anyway. With possible the same effects. – Kwebble Jul 14 '11 at 15:20
  • 1
    Assuming this is a real task (and not just a task with which a boss wants to see how you code ;)), this is another reason a boss/project manager has to be someone already involved in software development/testing. – Haris Osmanagić Feb 13 '14 at 12:42
  • 1
    You don't actually provide an answer to the question. And I don't see why you should not access private variables in your tests. How do you test immutable classes then? – ACV Dec 06 '20 at 00:13
36

Yeah you can use reflections to access private variables. Altough not a good idea.

Check this out:

http://en.wikibooks.org/wiki/Java_Programming/Reflection/Accessing_Private_Features_with_Reflection

Alfredo Osorio
  • 11,297
  • 12
  • 56
  • 84
25

Reflection e.g.:

public class PrivateObject {

  private String privateString = null;

  public PrivateObject(String privateString) {
    this.privateString = privateString;
  }
}
PrivateObject privateObject = new PrivateObject("The Private Value");

Field privateStringField = PrivateObject.class.
            getDeclaredField("privateString");

privateStringField.setAccessible(true);

String fieldValue = (String) privateStringField.get(privateObject);
System.out.println("fieldValue = " + fieldValue);
Mr-IDE
  • 7,051
  • 1
  • 53
  • 59
bpgergo
  • 15,669
  • 5
  • 44
  • 68
  • 1
    I get an error "no such field exception" when I try to use your technique. – IgorGanapolsky Oct 30 '12 at 17:29
  • 1
    the above approach works only if the field is declared in the target not, not in a parent class. There are other methods in the Class class that allow you to get your hands on parent declared fields instead of getDeclaredField(String). – Adrian B. Oct 05 '21 at 10:47
4

Despite the danger of stating the obvious: With a unit test you want to test the correct behaviour of the object - and this is defined in terms of its public interface. You are not interested in how the object accomplishes this task - this is an implementation detail and not visible to the outside. This is one of the things why OO was invented: That implementation details are hidden. So there is no point in testing private members. You said you need 100% coverage. If there is a piece of code that cannot be tested by using the public interface of the object, then this piece of code is actually never called and hence not testable. Remove it.

MKK
  • 670
  • 5
  • 13
0

If you create your test classes in a seperate folder which you then add to your build path,

Then you could make the test class an inner class of the class under test by using package correctly to set the namespace. This gives it access to private fields and methods.

But dont forget to remove the folder from the build path for your release build.

0

I can't tell if you've found some special case code which requires you to test against private fields. But in my experience you never have to test something private - always public. Maybe you could give an example of some code where you need to test private?

ThomasArdal
  • 4,999
  • 4
  • 33
  • 73
  • Unfortunately I cannot give example code. The testing of private members comes from a managerial requirement of having 100% code covering tests (as opposed to simply functional, or input/output tests). Not very useful if you ask me, but it is an unfortunate required protocol that is plaguing the programmers. – donnyton Jul 14 '11 at 18:20
  • 2
    Sound like your manager attended some crappy course, telling him/her that 100 % code coverage is a great idea. That just makes me sad. I feel with you :) – ThomasArdal Jul 15 '11 at 06:08
  • I made a class with a private constant that was used as a end of file hash for file saving. I have to pass the hash into the functions to make it testable though, because the program is supposed to load old files if it finds a bad hash, and the only way to test this is to feed it a new hash. Could probably call that bad design. – John Glen Feb 08 '21 at 22:57