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;
    }
}

      

+2


source to share


8 answers


Can you just mock the entity by providing your own getter implementations?

You can create an anonymous extension in your mock save layer:



MyEntity x = new MyEntity() {
    public Integer ixGet() { return new Integer(88); }
};

      

+8


source


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.

+8


source


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.

+3


source


You can use a test library like Mockito to access the internal state of objects in read and write mode. For example, using Mockito:

//read
Integer i = Whitebox.getInternalState(myEntity,"_ix")
//Write 
Whitebox.setInternalState(myEntity,"_ix", 123) 

      

+3


source


Some techniques I've used in the past:

  • Make _ix protected, create a subclass where you implement the setter
  • Make a constructor take a value for _ix as a parameter
  • Use reflection
+2


source


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

+2


source


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;
    }
}

      

+2


source


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.

+1


source







All Articles