Can I chain returns with PowerMockito using doRetu

2019-08-13 17:13发布

I have a method call that needs to return valueA the first time it is called and valueB the second time it is called. I'm using a PowerMockito spy, so if I were just needing to return one value it would look like this:

PowerMockito.doReturn(valueA).when(mockedObject, "methodName");

It looks like I can do chained returns like this:

PowerMockito.when(mockedObject, "methodName").thenReturn(valueA).thenReturn(valueB);

But I need to indicate chained returns with doReturn so that the real methodName() isn't called on my Spy.

I've tried this, but Eclipse gives me an error saying that it won't even compile:

PowerMockito.doReturn(valueA).doReturn(valueB).when(mockedObject, "methodName");

Is it even possible to chain returns with doReturn and powermockito? If so, how?

2条回答
三岁会撩人
2楼-- · 2019-08-13 17:28

Try this:

((PowerMockitoStubber) PowerMockito.doReturn(valueA).doReturn(valueB))
    .when(mockedObject, "methodName");

In short, I think you've found a hole in the PowerMockito API. This may be an excellent thing to submit as a pull request, or at least to file as a feature request.

What's happening is that org.powermock.api.mockito.PowerMockito.doReturn (et al) will return a PowerMockitoStubber implementation, which extends Mockito's Stubber; under the hood, PowerMockitoStubberImpl extends StubberImpl. Because PowerMock doesn't need to change the functionality, it does not override these calls; the second doReturn in PowerMockito.doReturn(foo).doReturn(bar) will invoke Mockito's StubberImpl and return a Mockito Stubber.

This is a problem, because in the transition to Stubber, you lose the PowerMockito when signatures, such as the one you need. In short, PowerMockito doVerb calls do support chaining, and do support referring to Methods reflectively or by name, but currently not both at the same time.


Internally, StubberImpl follows the builder pattern, returning itself after every call:

public Stubber doCallRealMethod() {
    answers.add(new CallsRealMethods());
    return this;
}

Because this refers to the PowerMockitoStubberImpl subclass, it would be easy to cast the Stubber to PowerMockitoStubber to get access to the additional methods. For the above workaround, you're making the cast yourself:

((PowerMockitoStubber) PowerMockito.doReturn(valueA).doReturn(valueB))
    .when(mockedObject, "methodName");

As a long term solution, because anything that returns PowerMockitoStubber necessarily returns Stubber, this may be possible to fix for all PowerMockito users purely through an interface override (noting the caveats listed on Joseph D. Darcy's Oracle Weblog). I haven't tested this, but it may be this easy:

/* in PowerMockitoStubber.java, for each doVerb method: */
@Override public PowerMockitoStubber doNothing();

At which point you would simply have to adapt the return type:

@Override public PowerMockitoStubber doNothing() {
  super.doNothing();
  return this;
}
查看更多
女痞
3楼-- · 2019-08-13 17:45

I don't think so. Rather, You can achieve it with using doAnswer and Queue like below

@Test
public void testReturnChain() throws Exception {
    Example example = new Example() {
        public String getValue() {
            return null;
        }
    };
    Example mockExample = spy(example);
    PowerMockito.doAnswer(new Answer<String>() {
        private final Queue<String> values = new LinkedList<String>(Arrays.asList("firstValue", "secondValue"));

        public String answer(InvocationOnMock invocationOnMock) throws Throwable {
            return values.poll();
        }
    }).when(mockExample, "getValue");

    System.out.println(mockExample.getValue());
    System.out.println(mockExample.getValue());
    System.out.println(mockExample.getValue());
}
查看更多
登录 后发表回答