How can I access the private members of a class in Java?
I have data model classes that contain read-only private fields (via a getter function). These fields are set by my JPA continuity provider (eclipselink) during normal operation using the contents of the database. For unit tests, I want to set their fake values ββfrom the save layer layout. How can i do this? How does eclipselink set these values ββanyway?
Simplified example:
@Entity
class MyEntity
{
@Id
private Integer _ix;
public Integer ixGet()
{
return this._ix;
}
}
source to share
You need to use the Reflection API. Use Class.getField () to get a field and then call setAccessable (true) on that field so you can write to it even if it is private, and finally you can call set () on it to write new meaning.
For example:
public class A {
private int i;
}
You want to set the 'i' field to 3 even though it's private:
void forceSetInt(Object o, String fieldName, int value) {
Class<?> clazz = o.getClass();
Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
field.set(o, value);
}
There are a number of exceptions that you will need to handle.
source to share
You can use a fake framework like powermock using pass encapsulation. In powermock you have to use Whitebox.setInternalState(..)
to set a private member.
A less invasive method would be to mock the getter method. Whether this is possible depends on what else depends on the internal state, but if that's enough, this is a cleaner solution.
source to share
Another option, if you really hate posting stuff, is to subclass it for testing and make it public there.
You have several options:
- Create stubs to replace your entity (extract the interface first)
- Using Reflection
- Add a public setter for testing
- Keep your tests inside a bundle and use the default scope
For tons of useful tricks, take a look at Michael Per's book, Working Effectively with Legacy Code
source to share
You can add a parameterized constructor to your read-only variable. Don't forget to add a default constructor (null parameter).
@Entity
class MyEntity
{
@Id
private Integer _ix;
public MyEntity(Integer ix) {
_ix = ix;
}
public MyEntity() {
/*
* Default constructor
*/
}
public Integer ixGet()
{
return this._ix;
}
}
source to share
Constructor is the best way I think. If this object is to be truly readonly (not allowed to create new instances at all in production code), you can make the constructor with package access and only use it within tests. And chances are that even if you make your default constructor private or with package access, your persistance provider will still be able to work with such an entity, but not sure though - check with eclipselink docs.
source to share