Статьи

Использование PowerMock для моделирования конструкторов

По моему мнению, одно из главных преимуществ внедрения зависимостей заключается в том, что вы можете внедрить фиктивные и / или заглушенные объекты в свой код, чтобы улучшить тестируемость, увеличить охват тестами и написать более качественные и более значимые тесты. Однако бывают случаи, когда вы сталкиваетесь с неким устаревшим кодом, который не использует внедрение зависимостей и удерживается вместе композицией, а не агрегацией.

Когда это происходит, у вас есть три варианта:

  1. Игнорируйте проблему и не пишите никаких тестов.
  2. Рефакторинг как безумный, меняющий все, чтобы использовать инъекцию зависимостей
  3. Используйте PowerMock, чтобы издеваться над конструкторами

Очевидно, что вариант 1 не является серьезным вариантом, и хотя я бы рекомендовал рефакторинг, чтобы перенести все на внедрение зависимостей, это требует времени и требует прагматичности. Вот где появляется PowerMock… этот блог демонстрирует, как использовать PowerMock для макета конструктора, а это означает, что когда ваш код вызывает новый, он не создает реальный объект, он создает фиктивный объект.

Чтобы продемонстрировать эту идею, первое, что нам нужно, это несколько классов для тестирования, которые показаны ниже.

1
2
3
4
5
6
7
public class AnyOldClass {
  
  public String someMethod() {
    return "someMethod";
  }
   
}
01
02
03
04
05
06
07
08
09
10
11
12
public class UsesNewToInstantiateClass {
 
  public String createThing() {
 
    AnyOldClass myclass = new AnyOldClass();
 
    String returnValue = myclass.someMethod();
    return returnValue;
    
  }
   
}

Первый класс, AnyOldClass, — это класс, экземпляр которого создается с помощью вызова new. В этом примере, как следует из названия, это может быть что угодно.

Второй класс, метко названный UsesNewToInstantiateClass, имеет один метод, createThing (), который при вызове делает:

1
AnyOldClass myclass = new AnyOldClass();

Это все довольно просто, поэтому мы быстро перейдем к тесту JUnit с помощью PowerMock:

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
import static org.easymock.EasyMock.expect;
import static org.junit.Assert.assertEquals;
import static org.powermock.api.easymock.PowerMock.expectNew;
import static org.powermock.api.easymock.PowerMock.replay;
import static org.powermock.api.easymock.PowerMock.verify;
 
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(UsesNewToInstantiateClass.class)
public class MockConstructorTest {
 
  @Mock
  private AnyOldClass anyClass;
 
  private UsesNewToInstantiateClass instance;
 
  @Test
  public final void testMockConstructor() throws Exception {
 
    instance = new UsesNewToInstantiateClass();
 
    expectNew(AnyOldClass.class).andReturn(anyClass);
 
    final String expected = "MY_OTHER_RESULT";
    expect(anyClass.someMethod()).andReturn(expected);
 
    replay(AnyOldClass.class, anyClass);
    String result = instance.createThing();
    verify(AnyOldClass.class, anyClass);
    assertEquals(expected, result);
    
  }
 
}

Во-первых, у этого класса есть обычные дополнения PowerMock:

1
2
@RunWith(PowerMockRunner.class)
@PrepareForTest(UsesNewToInstantiateClass.class)

в верхней части файла плюс создание фиктивного объекта anyOldClass. Важная строка кода для рассмотрения:

1
expectNew(AnyOldClass.class).andReturn(anyClass);

Эта строка кода сообщает PowerMock ожидать вызова нового AnyOldClass () и возвращать наш фиктивный объект anyClass.

Также интерес представляют звонки для воспроизведения и проверки. В приведенном выше примере они оба имеют два аргумента. Первый AnyOldClass.class относится к вышеприведенному вызову waitNew (…), а второй anyClass ссылается на прямое ожидание ложного вызова ожидаемого (anyClass.someMethod ()). AndReturn (ожидается) ;.

Есть моменты, когда вы действительно должны позволить new делать то, что он делает: создавать новый объект запрошенного типа. Существует мнение, что во время тестирования вы можете чрезмерно изолировать свой код, и что все, что имитируется, уменьшает значение и ценность теста. Для меня нет правильного ответа на это, и это вопрос выбора.

Совершенно очевидно, что если ваш код обращается к внешнему ресурсу, такому как база данных, то вы либо реорганизуете и внедряете DI, либо используете PowerMock. Если ваш тестируемый код не имеет доступа к каким-либо внешним ресурсам, то это скорее вопрос суждения о том, насколько много изоляции кода слишком много? Это, возможно, нуждается в некоторой мысли и может быть темой для другого блога в другой день …

Ссылка: Использование PowerMock для проверки конструкторов от нашего партнера по JCG Роджера Хьюза в блоге «Captain Debug» .