
Subverting Java Access Protection for Unit Testing
by Ross Burton11/12/2003
As everyone knows, unit testing is a great way of ensuring that code actually does what it claims it does, and that over time, as the system changes, it carries on doing the same thing. Formal processes like Extreme Programming (XP) depend heavily on consistent unit testing.
When used together, object-oriented design and comprehensive unit tests can lead to a very clean design, as the test-first methodology implies a user-based interface design, resulting in a public interface that is simple yet efficient.
However, when it comes to testing, sometimes these clean interfaces are not
as good as they could be. There are often member variables that the test
suite would like to access but that have been scoped private
or
protected
, and making these members public
would
expose the internals, ruining the clean design. C++ has a way of working
around this: by declaring the test suite as a friend
class,
the access protection is sidelined. In Java, a similar approach can be
used by making certain members package
scope and putting the
test classes into the same package. However, this leads to an
unsatisfactory design, as some members are private
or
protected
for good reasons, and then an arbitrary set of
members are package
scope solely for the current test suite.
However, there is a third option available to Java programmers. The Java
Reflection API allows the program to introspect arbitrary classes at
runtime, accessing members at will. Most competent Java
programmers should know this much, but what many don't know is that the security
layer preventing you from accessing private
members is only
optional, and can be turned off. This allows the system code to be
written without explicitly planning the unit tests, and if the unit tests
really do need to access a private
or protected
member, they can.
Example Files Download java-reflection-examples.tar.gz for the example code found in this article. |
Throughout this article, I shall deal with the simplest case: fields. In Java Reflection, fields, methods, and constructors are all treated in a very similar manner, so whenever I say "field" here, it can generally be replaced with "method" and the examples will still work.
Does private
Mean Private?
Let's begin by proving to ourselves that a private
field is
really a private field, and this is not just something we've all been told. First
we need a class with some private
members; this is the test
class I shall use for the following examples.
class FieldTest {
public String publicString = "Foobar";
private String privateString = "Hello, World!";
}
Here we have a simple class with two fields: one private
field and one public
field. We would assume that arbitrary
code can access the public
field, but not the
private
field.
public class Test1 {
public static void main(String args[]) {
System.out.println(new FieldTest().publicString);
System.out.println(new FieldTest().privateString);
}
}
When we compile this we get:
Test1.java:4: privateString has private access in FieldTest
System.out.println(new FieldTest().privateString);
^
1 error
This shows that private
fields really are private fields, as
the Java compiler won't allow access to them.
The Class Object
In Java 1.0, the java.lang.Class
object was fairly
trivial. However, in Java 1.1, the Reflection API was added. A cursory
glance at the documentation reveals several interesting methods:
getField()
getFields()
getDeclaredFields()
These methods return Field
instances (arrays, in the latter
two) that allow us to see the name of the field and its type, and more
importantly, get its value.
It seems that if we can get a Class
instance, we can call
getField()
to get a Field
instance, which we can
use to get at the value we want. And the easiest way to get a
Class
object for the class Foo
is to write
Foo.class
, a construct called a class literal.
Calling getField()
looks like this:
import java.lang.reflect.Field;
public class Test2 {
public static void main(String args[])
throws Exception {
Field f;
f = FieldTest.class.getField("publicString");
System.out.println("Public Field: " + f);
f = FieldTest.class.getField("privateString");
System.out.println("Private Field: " + f);
}
}
Running this gives us:
Public Field: public java.lang.String FieldTest.publicString
Exception in thread "main"
java.lang.NoSuchFieldException: privateString
at java.lang.Class.getField0(Class.java:1735)
at java.lang.Class.getField(Class.java:900)
at Test2.main(Test2.java:10)
So what happened here? The code managed to get a reference to
publicString
but failed when trying to get a reference to
privateString
. The exception thrown was
NoSuchFieldException
, but I know it does exist, since I created
it. However, the fine print in the API documentation for
Class.getField()
clearly states, "... the specified
public member field ...". Time to try
getFields()
and getDeclaredFields()
.
![]() |
Related Reading Java Extreme Programming Cookbook |
Enumerating Fields
At first glance, getFields()
and
getDeclaredFields()
seem very similar. Nevertheless, a closer
read of the API documentation reveals that they are very different.
The method getFields()
reflects (no pun intended) what the
Java programmer conceptually sees when programming: it enumerates all
publicly accessible fields in the class and all of its superclasses.
On the other hand, getDeclaredFields()
reveals how the class
is constructed. It enumerates fields, but only if they are actually
declared in that class; any inherited fields are ignored.
The reason for the existence of two methods (instead of a single method that returns
all fields, including inherited ones) seems to be so that simple dynamic
lookup of public
fields can be achieved easily (using
getField()
and getFields()
) and generally does
the right thing. If a program wants to see the private
fields,
it will probably want to handle inherited fields specially (for example,
an object-oriented debugger).
Exercising these methods is always a good thing, to check they do what we
expect. Here is a test for getFields()
.
import java.lang.reflect.Field;
public class Test3 {
public static void main(String args[]) {
final Field fields[] =
FieldTest.class.getFields();
for (int i = 0; i < fields.length; ++i) {
System.out.println("Field: " + fields[i]);
}
}
}
The output is rather predicable:
Field: public java.lang.String FieldTest.publicString
Now let's try the same test, but use getDeclaredFields()
instead.
import java.lang.reflect.Field;
public class Test4 {
public static void main(String args[]) {
final Field fields[] =
FieldTest.class.getDeclaredFields();
for (int i = 0; i < fields.length; ++i) {
System.out.println("Field: " + fields[i]);
}
}
}
We hope that this will yield all of the fields, both public
and private
:
Field: public java.lang.String FieldTest.publicString
Field: private java.lang.String FieldTest.privateString
Life is indeed good. Now that we can enumerate all of the fields in a class, we can get the specific field we are after and hopefully manipulate it however we want.
Pages: 1, 2 |
