Имитация частных методов, используемых в общедоступных методах, с помощью PowerMock, Mockito и TestNG

Я хочу написать модульный тест для класса, который у меня есть. У этого класса есть публичный метод, а внутри публичного метода есть вызовы приватных методов того же класса. Я хочу издеваться над вызовами этих частных методов. Класс похож на этот:

public class SomeClass {
    public int somePublicMethod(int num) {
        int num2 = somePrivateMethod1(num);
        int num3 = somePrivateMethod2(num);

        return num2 + num3;
    }

    private int somePrivateMethod1(int num) {
        return 2*num;
    }

    private int somePrivateMethod2(int num) {
        return 3*num;
    }
}

Для моего модульного теста я пытаюсь использовать PowerMock с Mockito и TestNG. Вот моя попытка теста, который проверяет somePublicMethod:

import static org.powermock.api.mockito.PowerMockito.doReturn;
import static org.powermock.api.mockito.PowerMockito.spy;

import org.powermock.core.classloader.annotations.PrepareForTest;
import org.testng.Assert;
import org.testng.annotations.Test;

@PrepareForTest(SomeClass.class)
public class SomeClassTest {

    @Test
    public void testSomePublicMethod() throws Exception {
        int num = 4;        

        SomeClass someClassSpy = spy(new SomeClass());

        doReturn(8).when(someClassSpy, "somePrivateMethod1", num);
        doReturn(12).when(someClassSpy, "somePrivateMethod2", num);

        int result = someClassSpy.somePublicMethod(num);

        Assert.assertEquals(result, 20);
    }
}

Когда я запускаю этот тест, я получаю исключение и некоторые подсказки:

FAILED: testSomePublicMethod
org.mockito.exceptions.misusing.UnfinishedStubbingException: 
Unfinished stubbing detected here:
-> at org.powermock.api.mockito.internal.PowerMockitoCore.doAnswer(PowerMockitoCore.java:31)

E.g. thenReturn() may be missing.
Examples of correct stubbing:
    when(mock.isOk()).thenReturn(true);
    when(mock.isOk()).thenThrow(exception);
    doThrow(exception).when(mock).someVoidMethod();
Hints:
 1. missing thenReturn()
 2. you are trying to stub a final method, you naughty developer!

Я просмотрел несколько примеров в Интернете, но не нашел ни одного, который использует PowerMock с Mockito и TestNG специально для того, чтобы делать то, что я хочу. Может ли кто-нибудь дать мне несколько советов о том, что я мог бы сделать по-другому?


person Thomas    schedule 09.05.2012    source источник


Ответы (3)


Я не думаю, что это проблема с PowerMock. Похоже, он должен работать нормально, как есть. Интересно, есть ли проблема с TestNG, взаимодействующим с PowerMock?

У меня нет опыта работы с TestNG, поэтому я попытался запустить класс в JUnit. SomeClass оставили как есть, SomeClassTest минимально модифицировали для использования JUnit:

import static org.powermock.api.mockito.PowerMockito.*;

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

@RunWith(PowerMockRunner.class)
@PrepareForTest(SomeClass.class)
public class SomeClassTest {

    @Test
    public void testSomePublicMethod() throws Exception {
        final int num = 4;

        final SomeClass someClassSpy = spy(new SomeClass());

        doReturn(8).when(someClassSpy, "somePrivateMethod1", num);
        doReturn(12).when(someClassSpy, "somePrivateMethod2", num);

        final int result = someClassSpy.somePublicMethod(num);

        Assert.assertEquals(result, 20);
    }
}

Тест проходит.

Вам нужно указать PowerMockRunner.class, используя @RunWIth или аналогичную аннотацию, как в JUnit? Если я удалю эту аннотацию, я получу то же сообщение об ошибке, которое вы опубликовали.

ИЗМЕНИТЬ:

По этой ссылке: http://code.google.com/p/powermock/wiki/TestNG_usage

Вам нужно указать TestNG использовать фабрику объектов PowerMock. Чтобы сделать это программно, добавьте в тестовый класс такой метод:

@ObjectFactory
public IObjectFactory getObjectFactory() {
    return new org.powermock.modules.testng.PowerMockObjectFactory();
}

или, чтобы быть в безопасности, вы также можете расширить PowerMockTestCase:

@PrepareForTest(SomeClass.class)
public class SomeClassTest extends PowerMockTestCase {
   //...
}
person Tom Tresansky    schedule 11.05.2012

Вы не можете сделать таким образом. Вы пытались использовать отражение?

Используя отражение, вы можете издеваться над приватными методами, издеваясь над методами вызова, или проще: вы можете изменить его на временное публичное, а затем после теста (возможно, в tearDown) — вы можете изменить его обратно на приватное.

Надеюсь, это поможет.

person nnhthuan    schedule 10.05.2012
comment
Можете ли вы дать фрагмент кода для того же? Как вернуть издевательский объект из частного метода? - person user3153014; 26.04.2018
comment
@ user3153014, хотя мы могли бы использовать отражение, но это не рекомендуется. Как вы знаете, приватный метод в любом случае должен вызываться одним из ваших публичных методов, поэтому лучше тестировать все из публичного метода. - person nnhthuan; 27.04.2018
comment
@user3153014 user3153014 Когда вы что-то реализуете, просто убедитесь, что вы пишете тестируемый код. Вы можете попробовать поискать в Google тестируемый и нетестируемый код, вы найдете несколько лучших практик и некоторые недостатки, которые нужно предотвратить. - person nnhthuan; 27.04.2018
comment
Да, частный метод вызывается из общедоступного метода, но проблема в том, что частный метод всегда будет давать исключение в тестовой среде, поскольку это метод для подключения к БД. - person user3153014; 27.04.2018
comment
@user3153014 user3153014 Изоляция - это самое важное в модульном тестировании, вам нужно изолировать его от других вещей, таких как другие службы, другие классы ... конечно, изолировать его от среды (базы данных) - person nnhthuan; 27.04.2018

Я думаю, что вы достигаете предела Mockito здесь. В подобных случаях я использую замену метода Powermock или объявляю целевой метод защищенным и переопределяю его при создании экземпляра тестируемого класса. Надеюсь, это поможет.

person Community    schedule 10.05.2012