2009-08-20 4 views
2

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

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

@Entity 
class MyEntity 
{ 
    @Id 
    private Integer _ix; 

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

ответ

8

Вы можете просто высмеиваете самое сущность, предоставляя свое собственное implemenations из добытчиков?

Вы можете создать анонимную расширение в вашем макете сохранение слоя:

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

Этот конкретный метод (переопределение сорбент анонимно) устраивает свою особую установку лучше, потому что у меня есть завод поставляя объекты для тестирования, так или иначе. Я не думал об этом, спасибо! –

+0

есть проблемы с этим, хотя - у него будут побочные эффекты –

+2

Просьба уточнить. Я использовал эту технику довольно широко, тестируя некоторые плагины eclipse, которые я написал. Не замечал никаких побочных эффектов, кроме теплого сияния счастья. – djna

2

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

  • Make _ix защищаемого, создать подкласс, где реализовать setter
  • Сделать конструктор, принимающий значение для _ix в качестве параметра
  • Использование отражения
+0

Хорошие предложения, спасибо! Хотя я хотел бы сохранить _ix частным. –

2

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

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

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

Для кучи полезных приемов взгляните на булочку Майкла Пера k, Working Effectively With Legacy Code

+0

создать подкласс для тестирования не работает. – frhack

8

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

Например:

public class A { 
    private int 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); 
} 

Есть целый ряд исключений, которые вы должны ручка.

+0

Обратите внимание, что я отвечаю на ваш вопрос буквально. Существует ряд решений, которые не требуют нарушения защиты языка Java. – Jonathan

+0

Я думаю, это то, что использует поставщик сохранения, не так ли? –

+0

Как правило, провайдеры настойчивости не форсируют свои права на разрешения. Они либо выберут записываемое «поле», либо они будут искать метод setField, который можно вызывать. – Jonathan

3

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

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

2

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

@Entity 
class MyEntity 
{ 
    @Id 
    private Integer _ix; 

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

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

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

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

2

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

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

Спасибо за это. Мы уже использовали Mockito, и я понятия не имел, что этот интерфейс Whitebox существует. – Merwer