Но что происходит, когда у вас нет внедрения зависимостей и вы используете стороннюю библиотеку, которая содержит классы определенного класса, содержащие статические методы? Один из способов — изолировать эти классы, написав обертку или адаптер вокруг них и используя это для обеспечения изоляции во время тестирования; Однако есть и другой способ: использование PowerMock. PowerMock — это фреймворк-фреймворк, расширяющий другие фреймворк-фреймворки для предоставления столь необходимой дополнительной функциональности Чтобы перефразировать старую рекламу: «она обновляет части, которые другие фальшивые рамки не могут достичь»
В этом блоге рассказывается о способности PowerMock имитировать статические методы, и приводится пример насмешки над классом JDK ResourceBundle , который, как многие из вас знают, использует ResourceBundle.getBundle (…) для, ну… загрузки пакетов ресурсов.
Я, как и многие другие блогеры и писатели, обычно представляю какой-то очень надуманный сценарий, чтобы осветить проблему. Сегодня все по-другому, у меня просто есть класс, который использует ResourceBundle с именем: UsesResourceBundle:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
public class UsesResourceBundle { private static Logger logger = LoggerFactory.getLogger(UsesResourceBundle. class ); private ResourceBundle bundle; public String getResourceString(String key) { if (isNull(bundle)) { // Lazy load of the resource bundle Locale locale = getLocale(); if (isNotNull(locale)) { this .bundle = ResourceBundle.getBundle( "SomeBundleName" , locale); } else { handleError(); } } return bundle.getString(key); } private boolean isNull(Object obj) { return obj == null ; } private Locale getLocale() { return Locale.ENGLISH; } private boolean isNotNull(Object obj) { return obj != null ; } private void handleError() { String msg = "Failed to retrieve the locale for this page" ; logger.error(msg); throw new RuntimeException(msg); } } |
Вы можете видеть, что есть один метод: getResourceString (…), который с учетом ключа извлекает строку ресурса из пакета. Чтобы сделать эту работу немного более эффективной, я лениво загрузил свой пакет ресурсов и после загрузки я вызываю bundle.getString (key), чтобы получить свой ресурс. Чтобы проверить это, я написал тест PowerMock JUnit:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
|
import static org.easymock.EasyMock.expect; import static org.junit.Assert.assertEquals; import static org.powermock.api.easymock.PowerMock.mockStatic; import static org.powermock.api.easymock.PowerMock.replayAll; import static org.powermock.api.easymock.PowerMock.verifyAll; import java.util.Locale; import java.util.MissingResourceException; import java.util.ResourceBundle; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.powermock.api.easymock.annotation.Mock; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; @RunWith (PowerMockRunner. class ) @PrepareForTest (UsesResourceBundle. class ) public class UsesResourceBundleTest { @Mock private ResourceBundle bundle; private UsesResourceBundle instance; @Before public void setUp() { instance = new UsesResourceBundle(); } @Test public final void testGetResourceStringAndSucceed() { mockStatic(ResourceBundle. class ); expect(ResourceBundle.getBundle( "SomeBundleName" , Locale.ENGLISH)).andReturn(bundle); final String key = "DUMMY" ; final String message = "This is a Message" ; expect(bundle.getString(key)).andReturn(message); replayAll(); String result = instance.getResourceString(key); verifyAll(); assertEquals(message, result); } @Test (expected = MissingResourceException. class ) public final void testGetResourceStringWithStringMissing() { mockStatic(ResourceBundle. class ); expect(ResourceBundle.getBundle( "SomeBundleName" , Locale.ENGLISH)).andReturn(bundle); final String key = "DUMMY" ; Exception e = new MissingResourceException(key, key, key); expect(bundle.getString(key)).andThrow(e); replayAll(); instance.getResourceString(key); } @Test (expected = MissingResourceException. class ) public final void testGetResourceStringWithBundleMissing() { mockStatic(ResourceBundle. class ); final String key = "DUMMY" ; Exception e = new MissingResourceException(key, key, key); expect(ResourceBundle.getBundle( "SomeBundleName" , Locale.ENGLISH)).andThrow(e); replayAll(); instance.getResourceString(key); } } |
В приведенном выше коде я сделал необычный шаг, включив операторы import. Это должно подчеркнуть, что мы используем PowerMock версии статики импорта, а не EasyMock. Если вы случайно импортируете статику EasyMock, то все это просто не будет работать.
Существует четыре простых шага в настройке теста, который проверяет статический вызов:
1. Используйте бегунок PowerMock JUnit:
1
|
@RunWith (PowerMockRunner. class ) |
2. Объявите тестовый класс, над которым мы издеваемся:
1
|
@PrepareForTest (UsesResourceBundle. class ) |
3. Сообщите PowerMock имя класса, который содержит статические методы:
1
|
mockStatic(ResourceBundle. class ); |
4. Настройте ожидания, сообщая PowerMock ожидать вызова статического метода:
1
|
expect(ResourceBundle.getBundle( "SomeBundleName" , Locale.ENGLISH)).andReturn(bundle); |
Все остальное — простое управление, вы устанавливаете ожидания для других вызовов стандартных методов и сообщаете PowerMock / EasyMock запустить тест, проверяя результаты:
1
2
3
4
5
6
7
|
final String key = "DUMMY" ; final String message = "This is a Message" ; expect(bundle.getString(key)).andReturn(message); replayAll(); String result = instance.getResourceString(key); verifyAll(); |
PowerMock может делать намного больше, например, конструировать насмешки и вызывать закрытые методы. Подробнее об этом позже, возможно …
Справка: Использование PowerMock для моделирования статических методов от нашего партнера JCG Роджер в блоге капитана Дебуга .
- Правила в JUnit 4.9 (бета 3)
- Тестирование внутреннего состояния объекта с помощью PowerMock
- Асинхронная обработка Servlet 3.0 для десятикратного увеличения пропускной способности сервера
- Тестирование с помощью Scala
- Инструменты Java: Оптимизация и анализ исходного кода
- Список учебных пособий по Java и Android