Как я могу получить доступ к закрытым членам класса в Java? - PullRequest
3 голосов
/ 20 августа 2009

У меня есть классы моделей данных, которые содержат закрытые поля, предназначенные только для чтения (через функцию получения). Эти поля устанавливаются моим провайдером JPA (eclipselink) во время нормальной работы с использованием содержимого базы данных. Для модульных тестов я хочу установить для них ложные значения из макета слоя постоянства. Как я могу это сделать? Как же eclipselink устанавливает эти значения?

Упрощенный пример:

@Entity
class MyEntity
{
    @Id
    private Integer _ix;

    public Integer ixGet()
    {
        return this._ix;
    }
}

Ответы [ 8 ]

8 голосов
/ 20 августа 2009

Вам необходимо использовать Reflection API. Используйте Class.getField () для получения поля, затем вызовите setAccessable (true) для этого поля, чтобы вы могли писать в него, даже если оно закрытое, и, наконец, вы можете вызвать set () для него, чтобы записать новое значение.

Например:

public class A {
    private int i;
}

Вы хотите установить для поля 'i' значение 3, хотя оно является приватным:

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

Существует ряд исключений, которые вам нужно будет обработать.

8 голосов
/ 20 августа 2009

Можете ли вы просто издеваться над самой сущностью, предоставляя собственные реализации геттеров?

Вы можете создать анонимное расширение в слое mock persistence:

MyEntity x = new MyEntity() {
    public Integer ixGet() { return new Integer(88); }
};
4 голосов
/ 08 мая 2016

Вы можете использовать тестовую библиотеку, такую ​​как Mockito, для доступа к внутреннему состоянию объектов в режиме чтения и записи. Например, с использованием Mockito:

//read
Integer i = Whitebox.getInternalState(myEntity,"_ix")
//Write 
Whitebox.setInternalState(myEntity,"_ix", 123) 
3 голосов
/ 20 августа 2009

Для обхода инкапсуляции можно использовать фреймворк, такой как powermock . В Powermock вы бы использовали Whitebox.setInternalState(..) для установки личного участника.

Менее инвазивным методом было бы издеваться над методом геттера. Будет ли это осуществимо, будет зависеть от того, что еще зависит от внутреннего состояния, но если этого достаточно, это более чистое решение.

2 голосов
/ 20 августа 2009

Вы можете добавить конструктор с параметром для вашей переменной только для чтения. Не забудьте добавить конструктор по умолчанию (нулевой параметр).

@Entity
class MyEntity
{
    @Id
    private Integer _ix;

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

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

    public Integer ixGet()
    {
        return this._ix;
    }
}
2 голосов
/ 20 августа 2009

Другой вариант, если вам очень не нравится делать что-либо публичным, - это создать подкласс для тестирования и предоставить к нему публичный доступ.

У вас есть несколько вариантов:

  • Создание заглушек для замены вашей сущности (сначала извлеките интерфейс)
  • Использовать отражение
  • Добавление публичного сеттера для тестирования
  • Храните свои тесты в пакете и используйте область действия по умолчанию

Чтобы получить множество полезных приемов, взгляните на книгу Майкла Фезера, Эффективная работа с устаревшим кодом

2 голосов
/ 20 августа 2009

Некоторые методы, которые я использовал в прошлом:

  • Сделать _ix защищенным, создать подкласс, в котором вы реализуете сеттер
  • Сделать конструктор, принимающий значение _ix в качестве параметра
  • Использовать отражение
1 голос
/ 20 августа 2009

Конструктор - лучший способ, я думаю. Если эта сущность должна быть действительно доступна только для чтения (вообще не разрешено создавать новые экземпляры в рабочем коде), вы можете создать конструктор с доступом к пакету и использовать его только в тестах. И есть вероятность, что даже если вы сделаете ваш конструктор по умолчанию частным или с доступом к пакету, ваш поставщик персистентности все еще сможет работать с такой сущностью, но не уверен, хотя - проверьте с помощью документации eclipselink.

...