«1. Обзор

Модульное тестирование с помощью мок-фреймворка уже давно признано полезной практикой, а фреймворк Mockito, в частности, доминировал на этом рынке в последние годы.

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

Здесь в игру вступает инфраструктура PowerMock.

PowerMockito — это API расширения PowerMock для поддержки Mockito. Он предоставляет возможности для работы с Java Reflection API простым способом для преодоления проблем Mockito, таких как отсутствие возможности имитировать окончательные, статические или частные методы.

Этот туториал познакомит вас с PowerMockito API и его применением в тестах.

2. Подготовка к тестированию с помощью PowerMockito

Первым шагом для интеграции поддержки PowerMock для Mockito является включение следующих двух зависимостей в файл Maven POM:

<dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-module-junit4</artifactId>
    <version>1.6.4</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-api-mockito</artifactId>
    <version>1.6.4</version>
    <scope>test</scope>
</dependency>

Далее нам нужно подготовить наши тестовые примеры. для работы с PowerMockito, применяя следующие две аннотации:

@RunWith(PowerMockRunner.class)
@PrepareForTest(fullyQualifiedNames = "com.baeldung.powermockito.introduction.*")

Элемент fullQualifiedNames в аннотации @PrepareForTest представляет собой массив полных имен типов, которые мы хотим имитировать. В этом случае мы используем имя пакета с подстановочным знаком, чтобы указать PowerMockito подготовить все типы в пакете com.baeldung.powermockito.introduction для имитации.

Теперь мы готовы использовать мощь PowerMockito.

3. Мок-конструкторы и финальные методы

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

public class CollaboratorWithFinalMethods {
    public final String helloMethod() {
        return "Hello World!";
    }
}

Сначала мы создаем фиктивный объект, используя API PowerMockito:

CollaboratorWithFinalMethods mock = mock(CollaboratorWithFinalMethods.class);

Затем устанавливаем ожидание, говорящее, что всякий раз, когда вызывается конструктор без аргументов этого класса, должен быть возвращен фиктивный экземпляр, а не настоящий:

whenNew(CollaboratorWithFinalMethods.class).withNoArguments().thenReturn(mock);

Давайте посмотрим, как эта имитация конструкции работает в действии, создав экземпляр класса CollaboratorWithFinalMethods с помощью его конструктора по умолчанию, а затем проверить поведение PowerMock:

CollaboratorWithFinalMethods collaborator = new CollaboratorWithFinalMethods();
verifyNew(CollaboratorWithFinalMethods.class).withNoArguments();

На следующем шаге ожидание устанавливается для конечного метода:

when(collaborator.helloMethod()).thenReturn("Hello Baeldung!");

Затем выполняется этот метод:

String welcome = collaborator.helloMethod();

Следующие утверждения подтверждают, что Метод helloMethod был вызван для объекта-сотрудника и возвращает значение, установленное ложным ожиданием: T object) может пригодиться. Это показано в разделе 5.

Mockito.verify(collaborator).helloMethod();
assertEquals("Hello Baeldung!", welcome);

4. Имитация статических методов

Предположим, мы хотим имитировать статические методы класса CollaboratorWithStaticMethods. Этот класс объявлен следующим образом:

Чтобы имитировать эти статические методы, нам нужно зарегистрировать включающий класс в PowerMockito API:

public class CollaboratorWithStaticMethods {
    public static String firstMethod(String name) {
        return "Hello " + name + " !";
    }

    public static String secondMethod() {
        return "Hello no one!";
    }

    public static String thirdMethod() {
        return "Hello no one again!";
    }
}

В качестве альтернативы мы можем использовать Mockito.spy(Class \u003cT\u003e class) для имитации конкретного, как показано в следующем разделе.

mockStatic(CollaboratorWithStaticMethods.class);

Затем можно задать ожидания для определения значений, которые методы должны возвращать при вызове:

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

when(CollaboratorWithStaticMethods.firstMethod(Mockito.anyString()))
  .thenReturn("Hello Baeldung!");
when(CollaboratorWithStaticMethods.secondMethod()).thenReturn("Nothing special");

Теперь это время выполнения первых двух методов:

doThrow(new RuntimeException()).when(CollaboratorWithStaticMethods.class);
CollaboratorWithStaticMethods.thirdMethod();

Вместо вызова членов реального класса указанные выше вызовы делегируются методам макета. Следующие утверждения доказывают, что фиктивный объект вступил в силу:

String firstWelcome = CollaboratorWithStaticMethods.firstMethod("Whoever");
String secondWelcome = CollaboratorWithStaticMethods.firstMethod("Whatever");

Мы также можем проверить поведение методов фиктивного объекта, включая количество вызовов метода. В этом случае первый метод вызывался дважды, а второй — никогда:

assertEquals("Hello Baeldung!", firstWelcome);
assertEquals("Hello Baeldung!", secondWelcome);

«

verifyStatic(Mockito.times(2));
CollaboratorWithStaticMethods.firstMethod(Mockito.anyString());
        
verifyStatic(Mockito.never());
CollaboratorWithStaticMethods.secondMethod();

«Примечание. Метод verifyStatic должен вызываться непосредственно перед любой проверкой статического метода, чтобы PowerMockito знал, что последовательный вызов метода — это то, что необходимо проверить.

Наконец, статический метод ThirdMethod должен генерировать исключение RuntimeException, как было объявлено в макете ранее. Это подтверждается ожидаемым элементом аннотации @Test:

@Test(expected = RuntimeException.class)
public void givenStaticMethods_whenUsingPowerMockito_thenCorrect() {
    // other methods   
       
    CollaboratorWithStaticMethods.thirdMethod();
}

5. Частичная имитация

Вместо того, чтобы имитировать весь класс, API PowerMockito позволяет имитировать его часть, используя шпионский метод. Следующий класс будет использоваться в качестве соавтора, чтобы проиллюстрировать поддержку PowerMock для частичного имитации:

public class CollaboratorForPartialMocking {
    public static String staticMethod() {
        return "Hello Baeldung!";
    }

    public final String finalMethod() {
        return "Hello Baeldung!";
    }

    private String privateMethod() {
        return "Hello Baeldung!";
    }

    public String privateMethodCaller() {
        return privateMethod() + " Welcome to the Java world.";
    }
}

Давайте начнем с имитации статического метода, который назван staticMethod в приведенном выше определении класса. Во-первых, используйте API PowerMockito, чтобы частично имитировать класс CollaboratorForPartialMocking и установить ожидание для его статического метода:

spy(CollaboratorForPartialMocking.class);
when(CollaboratorForPartialMocking.staticMethod()).thenReturn("I am a static mock method.");

Затем выполняется статический метод:

returnValue = CollaboratorForPartialMocking.staticMethod();

Поведение имитации проверяется следующим образом: ~~ ~

verifyStatic();
CollaboratorForPartialMocking.staticMethod();

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

assertEquals("I am a static mock method.", returnValue);

Теперь пришло время перейти к финальным и закрытым методам. Для того, чтобы проиллюстрировать частичное имитация этих методов, нам нужно создать экземпляр класса и указать API PowerMockito следить за ним:

CollaboratorForPartialMocking collaborator = new CollaboratorForPartialMocking();
CollaboratorForPartialMocking mock = spy(collaborator);

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

when(mock.finalMethod()).thenReturn("I am a final mock method.");
returnValue = mock.finalMethod();

Доказано поведение частичного имитации этого метода: значение, соответствующее ожиданиям:

Mockito.verify(mock).finalMethod();

Аналогичный процесс применяется к частному методу. Основное отличие состоит в том, что мы не можем напрямую вызвать этот метод из тестового примера. По сути, частный метод должен вызываться другими из того же класса. В классе CollaboratorForPartialMocking метод privateMethod должен вызываться методом privateMethodCaller, и мы будем использовать последний в качестве делегата. Начнем с ожидания и вызова:

assertEquals("I am a final mock method.", returnValue);

Насмешка над закрытым методом подтверждена:

when(mock, "privateMethod").thenReturn("I am a private mock method.");
returnValue = mock.privateMethodCaller();

Следующий тест гарантирует, что возвращаемое значение при вызове закрытого метода такое же, как и ожидание :

verifyPrivate(mock).invoke("privateMethod");

6. Заключение

assertEquals("I am a private mock method. Welcome to the Java world.", returnValue);

В этом руководстве представлено введение в API PowerMockito, демонстрирующее его использование для решения некоторых проблем, с которыми сталкиваются разработчики при использовании платформы Mockito.

Реализацию этих примеров и фрагментов кода можно найти в связанном проекте GitHub.

«