views:

428

answers:

7

I have data model classes that contain private fields which are meant to be read-only (via a getter function). These fields are set by my JPA persistence provider (eclipselink) during normal operation, using the contents of the database. For unit tests, I want to set them to fake values from a mockup of the persistence layer. How can I do that? How does eclipselink set these values, anyway?

Simplified example:

@Entity
class MyEntity
{
    @Id
    private Integer _ix;

    public Integer ixGet()
    {
        return this._ix;
    }
}
+5  A: 

Can you just Mock the Entity itself, providing your own implemenations of the getters?

You could create an anonymous extension in your mock persistence layer:

MyEntity x = new MyEntity() {
    public Integer ixGet() { return new Integer(88); }
};
djna
That particular method (overriding the getter anonymously) suits my particular setup best, because I have a factory delivering the objects for testing, anyway. I hadn't thought of that, thanks!
Hanno Fietz
there's issues with doing this though - it is going to have side effects
Michael Wiles
Please elaborate. I've used this technique quite extensively when testing some eclipse plugins I wrote. Didn't notice any side-effects, other than a warm glow of happiness.
djna
+2  A: 

Some methods I've used in the past:

  • Make _ix protected, create a subclass where you implement a setter
  • Make a constructor taking the value for _ix as a parameter
  • Use reflection
Nils-Petter Nilsen
Good suggestions, thanks! Although I'd like to keep _ix private.
Hanno Fietz
+2  A: 

Another option, if you really hate to make things public, is to create a subclass for testing, and provide public access there.

You have a few options:

  • Create stubs to replace your entity (extract an interface first)
  • Use Reflection
  • Add a public setter for testing
  • Keep your tests within the package and use a default scope

For a bunch of useful techniques, have a look at Michael Feather's book, Working Effectively With Legacy Code

Nader Shirazie
+3  A: 

You need to use the Reflection API. Use Class.getField() to get the field, then call setAccessable(true) on that field so that you may write to it, even though it is private, and finally you may call set() on it to write a new value.

For example:

public class A {
    private int i;
}

You want to set the field 'i' to 3, even though it is private:

void forceSetInt(Object o, String fieldName, int value) {
    Class<?> clazz = o.getClass();
    Field field = clazz.getField(fieldName);
    field.setAccessable(true);
    field.setInt(o, value);
}

There are a number of exceptions that you will need to handle.

Jonathan
Note that I am answering your question literally. There are a number of solutions to this that do not require breaking the Java language protections.
Jonathan
I guess, this is what the persistence provider uses, right?
Hanno Fietz
Generally persistence providers do not force their way around permissions. They will either choose a writable 'field', or they will look for a 'setField' method that is callable.
Jonathan
Well, they seem to be able to handle my entities with all db fields set to private, not all of them having setters and the setters that are there do not adhere to the beans naming conventions (it's always vSetField instead of setField to be consistent with all the rest of my code). I'm now wondering if I'm putting a performance penalty on my persistence by not playing by the rules.
Hanno Fietz
A: 

You can use a mocking framework like powermock to by pass encapsulation. In powermock you'd use Whitebox.setInternalState(..) to set a private member.

A less invasive method would be to mock the getter method. Whether this is feasible would depend on what else depends on the internal state but if it is enough, it's the cleaner solution.

VoidPointer
+2  A: 

You can add constructor with parameter for your read-only variable. Don't forget to add a default (zero parameter) constructor.

@Entity
class MyEntity
{
    @Id
    private Integer _ix;

    public MyEntity(Integer ix) {
        _ix = ix;
    }

    public MyEntity() {
        /*
         * Default constructor
         */
    }

    public Integer ixGet()
    {
        return this._ix;
    }
}
Vincnetas
+1  A: 

The constructor is a best way I think. If this entity has to be really readonly (not allowed to create new instances in production code at all) you can make constructor with package access and use it only within the tests. And there is a possibility that even if you make your default constructor private or with package access, your persistance provider still be able to work with such entity, but not sure though - check with eclipselink docs.

alsor.net