Mockito/PowerMockito:奇怪的存根异常

Mockito/PowerMockito: Weird Stubbing Exception

我明白了 org.mockito.exceptions.misusing.UnfinishedStubbingException 但根据我在互联网上可以找到的所有帖子和描述,它似乎没有意义。

异常方法指出 thenReturn 可能缺失,但事实并非如此。我在下面的示例代码中故意留下了两种方式:doReturnthenReturn。 None 他们成功了。两者都具有完全相同的异常消息。

此外,没有内联模拟。我准备了所有静态 classes 并正在使用 PowerMockitoRunner。

我找不到任何出路。谁能帮我看看是怎么回事?

编辑: 我忘了说我正在使用 Mockito 1.8.5 和 PowerMockito 1.4.10。

完全异常:

    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. although stubbed methods may return mocks, you cannot inline mock creation (mock()) call inside a thenReturn method (see issue 53)

    at br.com.tests.email.EnvioCompartilhamento.mockCaptcha(EnvioCompartilhamento.java:120)
    at br.com.tests.email.EnvioCompartilhamento.setup(EnvioCompartilhamento.java:60)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at org.junit.internal.runners.MethodRoadie.runBefores(MethodRoadie.java:132)
    at org.junit.internal.runners.MethodRoadie.runBeforesThenTestThenAfters(MethodRoadie.java:95)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.executeTest(PowerMockJUnit44RunnerDelegateImpl.java:294)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.runBeforesThenTestThenAfters(PowerMockJUnit44RunnerDelegateImpl.java:282)
    at org.junit.internal.runners.MethodRoadie.runTest(MethodRoadie.java:86)
    at org.junit.internal.runners.MethodRoadie.run(MethodRoadie.java:49)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.invokeTestMethod(PowerMockJUnit44RunnerDelegateImpl.java:207)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.runMethods(PowerMockJUnit44RunnerDelegateImpl.java:146)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.run(PowerMockJUnit44RunnerDelegateImpl.java:120)
    at org.junit.internal.runners.ClassRoadie.runUnprotected(ClassRoadie.java:33)
    at org.junit.internal.runners.ClassRoadie.runProtected(ClassRoadie.java:45)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.run(PowerMockJUnit44RunnerDelegateImpl.java:118)
    at org.powermock.modules.junit4.common.internal.impl.JUnit4TestSuiteChunkerImpl.run(JUnit4TestSuiteChunkerImpl.java:102)
    at org.powermock.modules.junit4.common.internal.impl.AbstractCommonPowerMockRunner.run(AbstractCommonPowerMockRunner.java:53)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:160)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:78)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:212)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:68)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)

我的测试class。代码行增加 10 x 10(或类似):

006 --> import br.com.common.MyProperties;
import br.com.struts.email.EnvioDeEmail;
import br.com.struts.email.forms.FormularioParaCompartilhamento;
import br.com.util.UrlUtil;
010 --> import br.com.popular.commons.Publications;
import br.com.popular.commons.utils.escenic.RetrievingObjects;
import com.captcha.Captcha;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
020 --> import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.PrintWriter;

import static org.junit.Assert.assertNull;
030 --> import static org.junit.Assert.fail;
import static org.mockito.Matchers.*;
import static org.powermock.api.mockito.PowerMockito.*;

040 --> @RunWith(PowerMockRunner.class)
@PrepareForTest({ Captcha.class, RetrievingObjects.class, UrlUtil.class })
public class EnvioCompartilhamento {

    @Mock
    private ActionMapping mapping;

    @Mock
    private HttpServletRequest request;

050 --> @Mock
    private HttpServletResponse response;

    private FormularioParaCompartilhamento formulario;

    @Before
    public void setup() throws NoSuchMethodException, NoSuchFieldException, IOException {

        mockStaticClasses();
        mockRequestBehavior();
    060 --> mockCaptcha();
        mockResponse();
        formulario = new FormularioParaCompartilhamento();
    }

    @Test
    public void compartilhamentoComSucesso() {

        formulario.setEmailTo("teste@teste.com");
        formulario.setIdArtigo("12345");
    070 --> formulario.setIsArtigo(true);
        formulario.setMessage("Corpo do email");
        formulario.setTitulo("Titulo");
        formulario.setUrl("http://www.google.com");
        formulario.setCaptcha("ABCD");

        EnvioDeEmail email = new EnvioDeEmail();
        final ActionForward resultado = email.compartilhamento(mapping, formulario, request, response);

        assertNull(resultado);
    080 --> }

    112 --> private void mockRequestBehavior() {

        when(request.getMethod()).thenReturn("POST");
        when(request.getHeader("X-FORWARDED-FOR")).thenReturn("User IP");
    }

    private void mockCaptcha() {

    120 --> HttpSession session = mock(HttpSession.class);
        doReturn(session).when(request).getSession();
        Captcha captcha = Mockito.mock(Captcha.class);
        doReturn(captcha).when(session).getAttribute("captcha");
        doReturn(true).when(captcha).isInputValid(anyString());
    }

    private void mockStaticClasses() {

        final MyProperties myProperties = mock(MyProperties.class);
    130 --> mockStatic(RetrievingObjects.class);
        when(RetrievingObjects.componentFromPublicationAtSystemScope(any(Publications.class), eq("EmailProperties"), eq(MyProperties.class))).
            thenReturn(myProperties);
        mockStatic(UrlUtil.class);
        doNothing().when(UrlUtil.class);
    }

    private void mockResponse() throws IOException {

        PrintWriter writer = mock(PrintWriter.class);
    140 --> doReturn(writer).when(response).getWriter();
    }

}
doNothing().when(UrlUtil.class);

这对 Mockito 或 PowerMock 没有任何意义;您需要指定要模拟的特定调用。这使得这个存根未完成。以 PowerMockito when docs 为例。

但是,Mockito 无法在这一行上判断您的存根未完成——它只能在您与其交互时引发错误,因此它仅检测错误条件稍后,在您的 mockCaptcha 方法中。

要解决此问题,请按如下方式完成 UrlUtil 存根(我指定 PowerMockito 以区别于 Mockito.doNothing,尽管看起来您的静态导入是正确的):

PowerMockito.doNothing().when(UrlUtil.class);
UrlUtil.methodYouWantToMock();

或者,要使 UrlUtil 默认抑制其所有行为,请删除 doNothing 行和 put a default answer into your mockStatic call:

mockStatic(UrlUtil.class, RETURNS_SMART_NULLS);