Skip to content

Commit

Permalink
Fixes #726 Mockito Public API Support
Browse files Browse the repository at this point in the history
Using Mockito API for stubbing and creating invocation.
  • Loading branch information
Arthur Zagretdinov authored and thekingn0thing committed Sep 10, 2017
1 parent db008a1 commit dac474e
Show file tree
Hide file tree
Showing 13 changed files with 146 additions and 129 deletions.
1 change: 0 additions & 1 deletion .gitignore
Expand Up @@ -10,4 +10,3 @@ repo
.idea
**/build/**
.gradle
**/classes/**
2 changes: 1 addition & 1 deletion build.gradle
Expand Up @@ -32,7 +32,7 @@ ext{
junitRulesVersion = "4.8.2"
testngVersion = "6.9.10"
xstreamVersion = "1.4.9"
mockitoVersion = "2.8.48"
mockitoVersion = "2.8.54"
servletVersion = "2.5"
systemRulesVersion = "1.16.0"
jacocoVersion = "0.7.7.201606060606"
Expand Down
4 changes: 4 additions & 0 deletions docs/changelog.txt
@@ -1,3 +1,7 @@
Change log 2.0.0
* Public Mockito support:
** Possible incapability due toString methods returns by default name of mocked.

Change log 1.7.1
* Fixed #832 Provide a new API for verifying static mock and deprecate old for Mockito

Expand Down
Expand Up @@ -16,10 +16,12 @@
*/
package org.powermock.api.mockito.internal;

import org.mockito.Mockito;
import org.mockito.internal.progress.MockingProgress;
import org.mockito.internal.progress.ThreadSafeMockingProgress;
import org.mockito.internal.verification.MockAwareVerificationMode;
import org.mockito.stubbing.Answer;
import org.mockito.stubbing.Stubber;
import org.mockito.verification.VerificationMode;
import org.powermock.api.mockito.expectation.PowerMockitoStubber;
import org.powermock.api.mockito.internal.expectation.PowerMockitoStubberImpl;
Expand All @@ -28,17 +30,21 @@
public class PowerMockitoCore {
@SuppressWarnings("rawtypes")
public PowerMockitoStubber doAnswer(Answer answer) {

// We change the context classloader to the current CL in order for the Mockito
// framework to load it's plugins (such as MockMaker) correctly.

final Stubber stubber;
final ClassLoader originalCL = Thread.currentThread().getContextClassLoader();

Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
try {
getMockingProgress().stubbingStarted();
stubber = Mockito.doAnswer(answer);
} finally {
Thread.currentThread().setContextClassLoader(originalCL);
}
getMockingProgress().resetOngoingStubbing();
return (PowerMockitoStubber) new PowerMockitoStubberImpl().doAnswer(answer);

return new PowerMockitoStubberImpl(stubber);
}

private MockingProgress getMockingProgress() {
Expand Down
Expand Up @@ -16,114 +16,121 @@
*/
package org.powermock.api.mockito.internal.expectation;

import org.mockito.internal.stubbing.StubberImpl;
import org.mockito.stubbing.Answer;
import org.mockito.stubbing.Stubber;
import org.powermock.api.mockito.expectation.PowerMockitoStubber;
import org.powermock.api.mockito.expectation.PrivatelyExpectedArguments;
import org.powermock.api.mockito.invocation.MockitoMethodInvocationControl;
import org.powermock.api.mockito.invocation.MockHandlerAdaptor;
import org.powermock.core.MockRepository;
import org.powermock.reflect.Whitebox;

import java.lang.reflect.Method;
import java.util.List;

/**
* Extension of the standard Mocktio stubber implementation that also support
* PowerMockito created mocks.
*/
public class PowerMockitoStubberImpl extends StubberImpl implements PowerMockitoStubber {

public class PowerMockitoStubberImpl implements PowerMockitoStubber, Stubber {

private final Stubber stubber;

public PowerMockitoStubberImpl(final Stubber stubber) {this.stubber = stubber;}

@Override
public void when(Class<?> classMock) {
MockitoMethodInvocationControl invocationControl = (MockitoMethodInvocationControl) MockRepository
.getStaticMethodInvocationControl(classMock);
addAnswersForStubbing(invocationControl);
}

/**
* Supports PowerMockito mocks. If {@code mock} is not a PowerMockito
* mock it will delegate to Mockito.
*
* @see Stubber#when(Object)
*/
@Override
public <T> T when(T instanceMock) {
MockitoMethodInvocationControl invocationControl = (MockitoMethodInvocationControl) MockRepository
.getInstanceMethodInvocationControl(instanceMock);
public <T> T when(final T instanceMock) {
final MockitoMethodInvocationControl invocationControl = (MockitoMethodInvocationControl) MockRepository.getInstanceMethodInvocationControl(instanceMock);
final T returnValue;
if (invocationControl == null) {
returnValue = super.when(instanceMock);
returnValue = stubber.when(instanceMock);
} else {
addAnswersForStubbing(invocationControl);
final Object mock = invocationControl.getMockHandlerAdaptor().getMock();
stubber.when(mock);
returnValue = instanceMock;
}
return returnValue;
}

@SuppressWarnings("unchecked")
private void addAnswersForStubbing(MockitoMethodInvocationControl invocationControl) {
final MockHandlerAdaptor mockHandler = invocationControl.getMockHandlerAdaptor();
final List<Answer<?>> answers = Whitebox.getInternalState(this, List.class);
mockHandler.setAnswersForStubbing(answers);

@Override
public Stubber doThrow(final Throwable... toBeThrown) {return stubber.doThrow(toBeThrown);}

@Override
public Stubber doThrow(final Class<? extends Throwable> toBeThrown) {return stubber.doThrow(toBeThrown);}

@Override
public Stubber doThrow(final Class<? extends Throwable> toBeThrown, final Class<? extends Throwable>[] nextToBeThrown) {return stubber.doThrow(toBeThrown, nextToBeThrown);}

@Override
public Stubber doAnswer(final Answer answer) {return stubber.doAnswer(answer);}

@Override
public Stubber doNothing() {return stubber.doNothing();}

@Override
public Stubber doReturn(final Object toBeReturned) {return stubber.doReturn(toBeReturned);}

@Override
public Stubber doReturn(final Object toBeReturned,
final Object... nextToBeReturned) {return stubber.doReturn(toBeReturned, nextToBeReturned);}

@Override
public Stubber doCallRealMethod() {return stubber.doCallRealMethod();}

@Override
public void when(Class<?> classMock) {
MockitoMethodInvocationControl invocationControl = (MockitoMethodInvocationControl) MockRepository.getStaticMethodInvocationControl(classMock);
final Object mock = invocationControl.getMockHandlerAdaptor().getMock();
stubber.when(mock);
}

@Override
public <T> PrivatelyExpectedArguments when(T mock, Method method) throws Exception {
assertNotNull(mock, "mock");
assertNotNull(method, "Method");
prepareForStubbing(mock);
this.when(mock);
return new DefaultPrivatelyExpectedArguments(mock, method);
}

@Override
public <T> void when(T mock, Object... arguments) throws Exception {
assertNotNull(mock, "mock");
prepareForStubbing(mock);
this.when(mock);
Whitebox.invokeMethod(mock, arguments);
}

@Override
public <T> void when(T mock, String methodToExpect, Object... arguments) throws Exception {
assertNotNull(mock, "mock");
assertNotNull(methodToExpect, "methodToExpect");
prepareForStubbing(mock);
this.when(mock);
Whitebox.invokeMethod(mock, methodToExpect, arguments);
}

@Override
public <T> void when(Class<T> classMock, Object... arguments) throws Exception {
assertNotNull(classMock, "classMock");
when(classMock);
Whitebox.invokeMethod(classMock, arguments);
}

@Override
public <T> void when(Class<T> classMock, String methodToExpect, Object... parameters) throws Exception {
assertNotNull(classMock, "classMock");
assertNotNull(methodToExpect, "methodToExpect");
when(classMock);
Whitebox.invokeMethod(classMock, methodToExpect, parameters);
}

@Override
public <T> PrivatelyExpectedArguments when(Class<T> classMock, Method method) throws Exception {
assertNotNull(classMock, "classMock");
assertNotNull(method, "Method");
when(classMock);
return new DefaultPrivatelyExpectedArguments(classMock, method);
}

private void assertNotNull(Object object, String name) {
if (object == null) {
throw new IllegalArgumentException(name + " cannot be null");
}
}

private <T> void prepareForStubbing(T mock) {
MockitoMethodInvocationControl invocationControl = (MockitoMethodInvocationControl) MockRepository.getInstanceMethodInvocationControl(mock);
addAnswersForStubbing(invocationControl);
}

}
Expand Up @@ -18,13 +18,9 @@

package org.powermock.api.mockito.invocation;

import org.mockito.internal.creation.DelegatingMethod;
import org.mockito.internal.debugging.LocationImpl;
import org.mockito.internal.invocation.InvocationImpl;
import org.mockito.internal.invocation.realmethod.CleanTraceRealMethod;
import org.mockito.internal.invocation.realmethod.RealMethod;
import org.mockito.internal.progress.SequenceNumber;
import org.mockito.Mockito;
import org.mockito.invocation.Invocation;
import org.mockito.mock.MockCreationSettings;
import org.powermock.api.support.SafeExceptionRethrower;
import org.powermock.core.MockGateway;
import org.powermock.core.MockRepository;
Expand All @@ -33,49 +29,33 @@
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.concurrent.Callable;

class InvocationFactory {

Invocation createInvocation(final Object interceptionObject, final Method method, final Object... arguments) {
final CleanTraceRealMethod cleanTraceRealMethod = createRealMethod(interceptionObject, method);

return new InvocationImpl(
interceptionObject,
new DelegatingMethod(method),
arguments,
SequenceNumber.next(),
cleanTraceRealMethod,
new LocationImpl()
);
Invocation createInvocation(final Object mock, final Method method, final MockCreationSettings settings,
final Object... arguments) {
final Callable realMethod = createRealMethod(mock, method, arguments);
return Mockito.framework().createInvocation(mock, settings, method, realMethod, arguments);
}

private CleanTraceRealMethod createRealMethod(final Object interceptionObject, final Method method) {
return new CleanTraceRealMethod(new RealMethod() {
private static final long serialVersionUID = 4564320968038564170L;

private Callable createRealMethod(final Object delegator, final Method method,
final Object... arguments) {
return new Callable() {
@Override
public Object invoke(Object target, Object[] arguments) throws Throwable {
/*
* Instruct the MockGateway to don't intercept the next call.
* The reason is that when Mockito is spying on objects it
* should call the "real method" (which is proxied by Mockito
* anyways) so that we don't end up in here one more time which
* causes infinite recursion. This should not be done if the
* interceptionObject is a final system class because these are
* never caught by the Mockito proxy.
*/
final Class<?> type = Whitebox.getType(interceptionObject);
public Object call() throws Exception {
final Class<?> type = Whitebox.getType(delegator);
final boolean isFinalSystemClass = type.getName().startsWith("java.") && Modifier.isFinal(type.getModifiers());
if (!isFinalSystemClass) {
MockRepository.putAdditionalState(MockGateway.DONT_MOCK_NEXT_CALL, true);
}
try {
return method.invoke(target, arguments);
return method.invoke(delegator, arguments);
} catch (InvocationTargetException e) {
SafeExceptionRethrower.safeRethrow(e.getCause());
}
return null;
}
});
};
}
}
Expand Up @@ -18,9 +18,10 @@

package org.powermock.api.mockito.invocation;

import org.mockito.MockingDetails;
import org.mockito.Mockito;
import org.mockito.exceptions.base.MockitoAssertionError;
import org.mockito.exceptions.misusing.NotAMockException;
import org.mockito.internal.util.MockUtil;
import org.mockito.invocation.Invocation;
import org.mockito.invocation.InvocationContainer;
import org.mockito.invocation.MockHandler;
Expand All @@ -36,12 +37,14 @@
* The class provides a access to method and data of {@link org.mockito.invocation.MockHandler} from the given mock instance.
*/
public class MockHandlerAdaptor<T> {
private final T mockInstance;
private final T mock;
private final InvocationFactory invocationFactory;
private final MockingDetails mockingDetails;

MockHandlerAdaptor(final T mockInstance) {
this.mockInstance = mockInstance;
MockHandlerAdaptor(final T mock) {
this.mock = mock;
this.invocationFactory = new InvocationFactory();
this.mockingDetails = Mockito.mockingDetails(mock);
}

public void setAnswersForStubbing(final List<Answer<?>> answers) {
Expand All @@ -53,17 +56,21 @@ public void setAnswersForStubbing(final List<Answer<?>> answers) {
}
}

private MockHandler<T> getMockHandler(){
return MockUtil.getMockHandler(mockInstance);
public Object getMock() {
return mock;
}

private MockHandler<T> getMockHandler() {
return mockingDetails.getMockHandler();
}

InvocationContainer getInvocationContainer() {
return getMockHandler().getInvocationContainer();
}

Object performIntercept(final Object interceptionObject, final Method method, Object[] arguments) throws Throwable {
Object performIntercept(final Object mock, final Method method, Object[] arguments) throws Throwable {

Invocation invocation = createInvocation(interceptionObject, method, arguments);
Invocation invocation = createInvocation(mock, method, arguments);

try {
return getMockHandler().handle(invocation);
Expand All @@ -82,7 +89,7 @@ Object performIntercept(final Object interceptionObject, final Method method, Ob
}
}

private Invocation createInvocation(final Object interceptionObject, final Method method, final Object[] arguments) {
return invocationFactory.createInvocation(interceptionObject, method, arguments);
private Invocation createInvocation(final Object mock, final Method method, final Object[] arguments) {
return invocationFactory.createInvocation(mock, method, getMockHandler().getMockSettings(), arguments);
}
}

0 comments on commit dac474e

Please sign in to comment.