Subverting Java Access Protection for Unit Testing
Pages: 1, 2
Using Field Objects
Time to investigate what the Field object can do for us.
There are the classic get() and set() methods, a
large array of convenience methods to handle the getting/setting of a
particular primitive type (to avoid having to wrap the primitive in an
object manually), and a few methods to determine what the field is
(getName(), getModifiers()).
It is the get() method that we need, as we want to retrieve
the value of a String field. This method takes a reference to
the object that that we want to get the field from (this object is ignored
if the field is static). So this next example should work
fine:
import java.lang.reflect.Field;
public class Test5 {
public static void main(String args[])
throws Exception {
final Field fields[] =
FieldTest.class.getDeclaredFields();
for (int i = 0; i < fields.length; ++i) {
if ("privateString".equals(fields[i].getName())) {
System.out.println(
fields[i].get(new FieldTest()));
break;
}
}
}
}
However, running this produces an exception:
Exception in thread "main" java.lang.IllegalAccessException:
Class Test5 can not access a member of class FieldTest
with modifiers "private"
at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:57)
at java.lang.reflect.Field.doSecurityCheck(Field.java:811)
at java.lang.reflect.Field.getFieldAccessor(Field.java:758)
at java.lang.reflect.Field.get(Field.java:228)
at Test5.main(Test5.java:8)
And everything was looking so good. However, there is hope. Another
reading of the fine print in the API documentation leads to the
interesting line "If this Field object enforces
Java language access control, and the underlying field is inaccessible,
the method throws an IllegalAccessException" (emphasis
added). If enforcing access control is optional, there has to be a way to
turn it off.
The Security Manager
The Security Manager in Java is designed to stop arbitrary code from performing unwanted actions. It is primarily used in controlled environments, such as Java applets, where you wouldn't trust the applet to read files from your disk (since it could send your password database to a cracker). However, in the common case of a Java application executed from the command line, the security manager imposes few restrictions.
The exception we're getting comes from Java denying access to a
private field, as the field is "not
accessible." This check doesn't involve the Security Manager, but
simply calls isAccessible() on the superclass of
Field, AccessibleObject. The accessibility of a
private field defaults to false, but a call to
setAccessible() will perform the appropriate security checks
on my credentials and hopefully give me access.
import java.lang.reflect.Field;
public class Test6 {
public static void main(String args[])
throws Exception {
final Field fields[] =
FieldTest.class.getDeclaredFields();
for (int i = 0; i < fields.length; ++i) {
if ("privateString".equals(fields[i].getName())) {
fields[i].setAccessible(true);
System.out.println(
fields[i].get(new FieldTest()));
break;
}
}
}
}
The only difference between this and Test5.java is the addition of the setAccessible() call. When we run this test we see:
Hello, World!
Which is exactly what we were after: we can access a private
field and see its value. Very similar code should allow us to edit this
field as well.
import java.lang.reflect.Field;
public class Test7 {
public static void main(String args[])
throws Exception {
final Field fields[] =
FieldTest.class.getDeclaredFields();
for (int i = 0; i < fields.length; ++i) {
if ("privateString".equals(fields[i].getName())) {
FieldTest fieldTest = new FieldTest();
Field f = fields[i];
f.setAccessible(true);
System.out.println(f.get(fieldTest));
f.set(fieldTest, "Modified Field");
System.out.println(f.get(fieldTest));
break;
}
}
}
}
This code gives the predictable outcome:
Hello, World!
Modified Field
Note that no amount of code will allow you to change the value of a
final field.
The Security Manager, Part 2
As I said earlier, the Java Security Manager only lets you access
private fields if you have appropriate rights. Code that I
run on my desktop is trusted in some sense, as I have to explicity run
it. However, Java applets can be hidden on web pages and executed without
the user knowing, so these are untrusted.
This can be demonstrated by re-writing Test6.java as an applet. This involves two parts, the actual Java code:
import java.lang.reflect.Field;
import java.applet.Applet;
import java.awt.Label;
public class Test8 extends Applet {
public Test8() {
super();
String s = "Field not found";
try {
final Field fields[] =
FieldTest.class.getDeclaredFields();
for (int i = 0; i < fields.length; ++i) {
if ("privateString".equals(fields[i].getName())) {
fields[i].setAccessible(true);
s = (String)fields[i].get(new FieldTest());
break;
}
}
} catch (Exception ex) {
s = ex.getMessage();
}
add(new Label(s));
}
}
and an HTML page to load it:
<html>
<body>
<applet width="100" height="50"
code="Test8.class">
</applet>
</body>
</html>
It's not exactly a great example of applet programming, but it does the
job. Now, either load this HTML file into either a Java-enabled web
browser, or use the appletviewer that is supplied with the
JDK. The result is shown in Figure 1.

Figure 1. Accessing private fields from an applet
As expected, attempting to access the
private field in a secure context has been blocked. The only
way around this is to "sign" the applet, so that the user can
see who wrote the code, and decide whether they trust them or
not. However, signing .jar files is beyond the scope of this
article, and best saved for another day.
Methods and Constructors
So far I have only shown examples of accessing fields via reflection.
However, to prove that this concept does work for methods and
constructors, here's an example of calling a private method.
import java.lang.reflect.Method;
class MethodTest {
private final String sayHello(final String name) {
return "Hello, " + name;
}
}
public class Test9 {
public static void main(String args[])
throws Exception {
MethodTest test = new MethodTest();
final Method[] methods =
test.getClass().getDeclaredMethods();
for (int i = 0; i < methods.length; ++i) {
if (methods[i].getName().equals("sayHello")) {
final Object params[] = {"Ross"};
methods[i].setAccessible(true);
Object ret = methods[i].invoke(test, params);
System.out.println(ret);
}
}
}
}
As you can see, getting a list of the declared methods is straightforward, using
getDeclaredMethods(). Then the array is searched as before,
and the correct method made accessible. The method that does the magic
is invoke(). You call this on a Method object,
passing it the instance it should call the method on (unless it is a
static method, in which case you pass null), and an
Object[] of parameters to the method to be called. If the
method is void, then either an empty array or
null is acceptable. Java will also automatically marshal to
and from primitive types, so, for example, if you have a method int
round (final float f) that takes a float and
returns an int, the parameter array would have a single
instance of java.lang.Float and an instance of
java.lang.Integer would be returned.
Conclusion
Now that the Reflection API has been well exercised, let's bring it back to the field of unit testing. Say I wanted to write a unit test for FieldTest. Checking that the public field is the correct string is trivial:
import java.lang.reflect.Field;
import junit.framework.TestCase;
public class Test10 extends TestCase {
public Test10(final String name) {
super(name) ;
}
public void test_reflection() throws Exception {
FieldTest f = new FieldTest();
assertEquals (f.publicString, "Foobar");
}
}
When this test case is executed, it works as expected.
$ java Test10
.
Time: 0.036
OK (1 test)
I could copy the loop I've been using to get a reference to the private
field into the test case, but that would be very ugly if there were 10
private fields I wanted to check. Instead, a utility class is in
order. Here is PrivateAccessor.java:
import java.lang.reflect.Field;
import junit.framework.Assert;
/**
* Provides access to private members in classes.
*/
public class PrivateAccessor {
public static Object getPrivateField (Object o,
String fieldName) {
/* Check we have valid arguments */
Assert.assertNotNull(o);
Assert.assertNotNull(fieldName);
/* Go and find the private field... */
final Field fields[] =
o.getClass().getDeclaredFields();
for (int i = 0; i < fields.length; ++i) {
if (fieldName.equals(fields[i].getName())) {
try {
fields[i].setAccessible(true);
return fields[i].get(o);
} catch (IllegalAccessException ex) {
Assert.fail ("IllegalAccessException accessing " +
fieldName);
}
}
}
Assert.fail ("Field '" + fieldName +
"' not found");
return null;
}
}
The core of PrivateAccessor.java is the same as the previous
examples, but the code is more reliable, as it checks the inputs and uses
the Assert class to integrate with JUnit test cases. With
this class, when a test case needs access to a private field, it can use
getPrivateField to get a reference to the field, and the test
case does the right thing automatically if the field was not found or the
arguments were not valid. The test case can then assume that is has been
given a valid object, removing some error checking code. I now present
the final example, a JUnit test suite that tests FieldTest
and uses PrivateAccessor:
import java.lang.reflect.Field;
import junit.framework.TestCase;
public class Test11 extends TestCase {
public Test11(final String name) {
super(name) ;
}
public void test_reflection() throws Exception {
FieldTest f = new FieldTest();
/* Assert we have a valid object */
assertNotNull(f);
/* Assert that the public field is "Foobar" */
assertEquals (f.publicString, "Foobar");
String s =
(String) PrivateAccessor.getPrivateField(f,
"privateString");
assertEquals (s, "Hello, World!");
}
public static void main(String args[]) {
junit.textui.TestRunner.run (Test11.class);
}
}
And to prove that this works:
$ java Test11
.
Time: 0.039
OK (1 test)
By using PrivateAccessor, we can write our classes without having to
compromise the visibility of fields and other members just for the sake
of JUnit tests. This way, we can do right by our sense of good
object-oriented design, and still expose our classes to automated testing code.
Links
The following links will be helpful for background or further reading.
Ross Burton codes Java and embedded systems on Linux and enjoys Python, C, GTK+, and Debian GNU/Linux.
Return to ONJava.com.
-
reason for getDeclaredFields()
2009-02-19 09:29:16 mgobeil [View]
-
JUnit test and reflection
2007-09-10 20:44:38 Mortoza [View]
-
Great article
2006-05-21 19:55:40 JeffLowery [View]
-
how to access inherited fields
2006-02-13 07:39:47 Ulrich_Scholz [View]
-
how to access inherited fields
2006-03-16 03:29:41 luano [View]
- Trackback from http://blogs.blackmarble.co.uk/bm-bloggers/posts/204.aspx
A Java implementation of the GUITester
2005-10-19 23:57:28 [View]
- Trackback from http://blogs.blackmarble.co.uk/bm-bloggers/posts/204.aspx
A Java implementation of the GUITester
2005-09-14 11:53:26 [View]
- Trackback from http://blogs.blackmarble.co.uk/bm-bloggers/posts/204.aspx
A Java implementation of the GUITester
2005-08-10 14:35:38 [View]
- Trackback from http://people.etango.com/~markm/archives/000147.html
Unit Testing Private Fields and Methods
2004-07-24 10:51:03 [View]
-
Possible Usage: Inserting mock objects
2004-05-19 03:27:28 mdaf00 [View]
-
A simpler way
2004-01-07 12:04:07 anonymous2 [View]
-
A simpler way
2005-08-17 12:18:35 ho_00@yahoo.com [View]
-
Good reading about Reflection API but not a good solution for Unit testing
2003-11-26 06:09:38 anonymous2 [View]
-
Good reading about Reflection API but not a good solution for Unit testing
2007-03-07 14:41:04 drmpf [View]
-
I don't agree...
2003-11-26 05:25:56 anonymous2 [View]
- Trackback from http://www.jroller.com/page/gwyn/20031120#bypassing_java_access_protection
Bypassing Java Access Protection
2003-11-20 01:37:11 [View]
-
Use getters
2003-11-20 01:01:42 anonymous2 [View]
-
Alternatives to reflection
2003-11-14 01:56:04 anonymous2 [View]
-
Alternatives to reflection
2003-11-14 06:15:01 anonymous2 [View]
-
Public Introductions for Private Methods
2003-11-13 13:05:51 anonymous2 [View]
-
Public Introductions for Private Methods
2010-01-04 23:52:18 sw1527 [View]
-
Public Introductions for Private Methods
2003-11-21 16:06:55 anonymous2 [View]
-
Public Introductions for Private Methods
2005-10-05 08:53:29 pizza22005 [View]
-
PrivateAccessor as a subclass
2003-11-13 12:13:46 anonymous2 [View]
- Trackback from http://people.etango.com/~markm/archives/2003/11/13/unit_testing_private_fields_and_methods.html
Unit Testing Private Fields and Methods
2003-11-13 11:46:11 [View]