Skip to content

Commit

Permalink
Fixes #726 Mockito Public API Support
Browse files Browse the repository at this point in the history
Replace dirty approach for checking if call to real method was handled by mockito.
  • Loading branch information
Arthur Zagretdinov authored and thekingn0thing committed Sep 10, 2017
1 parent 12dcba0 commit 5faebe2
Show file tree
Hide file tree
Showing 12 changed files with 267 additions and 61 deletions.
Expand Up @@ -50,7 +50,7 @@
* @see Mockito
*/
public class PowerMockito extends MemberModifier {

private static final PowerMockitoCore POWERMOCKITO_CORE = new PowerMockitoCore();

/**
Expand Down Expand Up @@ -192,8 +192,7 @@ public static <T> T mock(Class<T> classToMock, MockSettings mockSettings) {
*/
@SuppressWarnings("unchecked")
public static synchronized <T> T spy(T object) {
MockSettings mockSettings = Mockito.withSettings().defaultAnswer(Mockito.CALLS_REAL_METHODS);
return DefaultMockCreator.mock((Class<T>) Whitebox.getType(object), false, true, object, mockSettings, (Method[]) null);
return POWERMOCKITO_CORE.spy(object);
}

/**
Expand Down Expand Up @@ -267,20 +266,18 @@ public static synchronized <T> void verifyStatic(Class<T> mockedClass, Verificat
/**
* Verify a private method invocation for an instance.
*
* @throws Exception If something unexpected goes wrong.
* @see Mockito#verify(Object)
*/
public static PrivateMethodVerification verifyPrivate(Object object) throws Exception {
public static PrivateMethodVerification verifyPrivate(Object object) {
return verifyPrivate(object, times(1));
}

/**
* Verify a private method invocation with a given verification mode.
*
* @throws Exception If something unexpected goes wrong.
* @see Mockito#verify(Object)
*/
public static PrivateMethodVerification verifyPrivate(Object object, VerificationMode verificationMode) throws Exception {
public static PrivateMethodVerification verifyPrivate(Object object, VerificationMode verificationMode) {
Mockito.verify(object, verificationMode);
return new DefaultPrivateMethodVerification(object);
}
Expand All @@ -299,10 +296,9 @@ public static PrivateMethodVerification verifyPrivate(Class<?> clazz) throws Exc
* Verify a private method invocation for a class with a given verification
* mode.
*
* @throws Exception If something unexpected goes wrong.
* @see Mockito#verify(Object)
*/
public static PrivateMethodVerification verifyPrivate(Class<?> clazz, VerificationMode verificationMode) throws Exception {
public static PrivateMethodVerification verifyPrivate(Class<?> clazz, VerificationMode verificationMode) {
return verifyPrivate((Object) clazz, verificationMode);
}

Expand Down Expand Up @@ -367,20 +363,18 @@ public static <T> OngoingStubbing<T> when(Object instance, String methodName, Ob
/**
* Expect calls to private methods.
*
* @throws Exception If something unexpected goes wrong.
* @see PowerMockito#when(Object)
*/
public static <T> WithOrWithoutExpectedArguments<T> when(Object instance, Method method) throws Exception {
public static <T> WithOrWithoutExpectedArguments<T> when(Object instance, Method method) {
return new DefaultMethodExpectationSetup<T>(instance, method);
}

/**
* Expect calls to private static methods.
*
* @throws Exception If something unexpected goes wrong.
* @see PowerMockito#when(Object)
*/
public static <T> WithOrWithoutExpectedArguments<T> when(Class<?> cls, Method method) throws Exception {
public static <T> WithOrWithoutExpectedArguments<T> when(Class<?> cls, Method method) {
return new DefaultMethodExpectationSetup<T>(cls, method);
}

Expand Down Expand Up @@ -420,7 +414,7 @@ public static <T> OngoingStubbing<T> when(Class<?> klass, Object... arguments) t
}

/**
* Just delegates to the original {@link PowerMockito#when(Object)} method.
* Just delegates to the original {@link Mockito#when(Object)} method.
*
* @see PowerMockito#when(Object)
*/
Expand Down Expand Up @@ -460,8 +454,8 @@ public static synchronized <T> ConstructorExpectationSetup<T> whenNew(String ful

/**
* Checks if any of given mocks (can be both instance and class mocks) has
* any unverified interaction. Delegates to the orignal
* {@link PowerMockito#verifyNoMoreInteractions(Object...)} if the mock is not a
* any unverified interaction. Delegates to the original
* {@link Mockito#verifyNoMoreInteractions(Object...)} if the mock is not a
* PowerMockito mock.
* <p>
* You can use this method after you verified your mocks - to make sure that
Expand All @@ -477,7 +471,7 @@ public static synchronized <T> ConstructorExpectationSetup<T> whenNew(String ful
* often, even in every test method. verifyNoMoreInteractions() is not
* recommended to use in every test method. verifyNoMoreInteractions() is a
* handy assertion from the interaction testing toolkit. Use it only when
* it's relevant. Abusing it leads to overspecified, less maintainable
* it's relevant. Abusing it leads to over-specified, less maintainable
* tests. You can find further reading <a href=
* "http://monkeyisland.pl/2008/07/12/should-i-worry-about-the-unexpected/"
* >here</a>.
Expand Down Expand Up @@ -513,7 +507,7 @@ public static void verifyNoMoreInteractions(Object... mocks) {

/**
* Verifies that no interactions happened on given mocks (can be both
* instance and class mocks). Delegates to the orignal
* instance and class mocks). Delegates to the original
* {@link PowerMockito#verifyNoMoreInteractions(Object...)} if the mock is not a
* PowerMockito mock.
* <p>
Expand Down Expand Up @@ -705,7 +699,7 @@ public static PowerMockitoStubber doNothing() {
* doReturn(&quot;bar&quot;).when(mock).foo();
* </pre>
* <p>
* Above scenarios shows a tradeoff of Mockito's ellegant syntax. Note that
* Above scenarios shows a trade off of Mockito's elegant syntax. Note that
* the scenarios are very rare, though. Spying should be sporadic and
* overriding exception-stubbing is very rare.
* <p>
Expand Down Expand Up @@ -759,7 +753,7 @@ public static PowerMockitoStubber doReturn(Object toBeReturned) {
* <p>
* Above scenarios shows a trade-off of Mockito's elegant syntax. Note that the scenarios are very rare, though.
* Spying should be sporadic and overriding exception-stubbing is very rare. Not to mention that in general
* overridding stubbing is a potential code smell that points out too much stubbing.
* overriding stubbing is a potential code smell that points out too much stubbing.
* <p>
* See examples in javadoc for {@link PowerMockito} class
*
Expand Down
Expand Up @@ -16,25 +16,31 @@
*/
package org.powermock.api.mockito.internal;

import org.mockito.MockSettings;
import org.mockito.Mockito;
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;
import org.powermock.api.mockito.internal.invocation.MockitoNewInvocationControl;
import org.powermock.api.mockito.internal.mockcreation.DefaultMockCreator;
import org.powermock.api.mockito.internal.stubbing.PowerMockCallRealMethod;
import org.powermock.api.mockito.internal.verification.DefaultConstructorArgumentsVerification;
import org.powermock.core.MockRepository;
import org.powermock.core.classloader.ClassloaderWrapper;
import org.powermock.core.spi.NewInvocationControl;
import org.powermock.reflect.Whitebox;

import java.lang.reflect.Method;
import java.util.concurrent.Callable;

import static org.powermock.utils.Asserts.assertNotNull;

public class PowerMockitoCore {

private static final PowerMockCallRealMethod POWER_MOCK_CALL_REAL_METHOD = new PowerMockCallRealMethod();

private static final String NO_OBJECT_CREATION_ERROR_MESSAGE_TEMPLATE = "No instantiation of class %s was recorded during the test. Note that only expected object creations (e.g. those using whenNew(..)) can be verified.";

public PowerMockitoStubber doAnswer(final Answer answer) {
Expand Down Expand Up @@ -95,14 +101,21 @@ public <T> DefaultConstructorArgumentsVerification<T> verifyNew(final Class<T> m
assertNotNull(mock, "Class to verify cannot be null");
assertNotNull(mode, "Verify mode cannot be null");

MockitoNewInvocationControl<T> invocationControl = (MockitoNewInvocationControl<T>) MockRepository.getNewInstanceControl(mock);
@SuppressWarnings("unchecked") MockitoNewInvocationControl<T> invocationControl = (MockitoNewInvocationControl<T>) MockRepository.getNewInstanceControl(mock);

assertNotNull(invocationControl, String.format(NO_OBJECT_CREATION_ERROR_MESSAGE_TEMPLATE, Whitebox.getType(mock).getName()));

invocationControl.verify(mode);
//noinspection unchecked
return new DefaultConstructorArgumentsVerification<T>((NewInvocationControl<T>) invocationControl, mock);
}

public <T> T spy(final T object) {
MockSettings mockSettings = Mockito.withSettings().defaultAnswer(POWER_MOCK_CALL_REAL_METHOD);
//noinspection unchecked
return DefaultMockCreator.mock((Class<T>) Whitebox.getType(object), false, true, object, mockSettings, (Method[]) null);
}

private PowerMockitoStubber doAnswer(final Callable<Stubber> callable) {
final Stubber stubber = ClassloaderWrapper.runWithClass(callable);
return new PowerMockitoStubberImpl(stubber);
Expand Down
@@ -0,0 +1,40 @@
/*
*
* Copyright 2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

package org.powermock.api.mockito.internal.stubbing;


public class MockitoRealMethodInvocation {
private static final ThreadLocal<Boolean> handledByMockito = new ThreadLocal<Boolean>();

private MockitoRealMethodInvocation() {
}

public static void mockitoInvocationStarted() {
handledByMockito.set(true);
}

public static void mockitoInvocationFinished() {
handledByMockito.set(false);
}

public static boolean isHandledByMockito() {
final Boolean handled = handledByMockito.get();
return handled == null ? false : handled;
}
}
@@ -0,0 +1,35 @@
/*
*
* Copyright 2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

package org.powermock.api.mockito.internal.stubbing;

import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;

public class PowerMockCallRealMethod implements Answer {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
MockitoRealMethodInvocation.mockitoInvocationStarted();
try {
return Mockito.CALLS_REAL_METHODS.answer(invocation);
} finally {
MockitoRealMethodInvocation.mockitoInvocationFinished();
}
}
}
Expand Up @@ -17,8 +17,8 @@
package org.powermock.api.mockito.internal.verification;

import org.mockito.Mockito;
import org.powermock.api.mockito.invocation.MockitoMethodInvocationControl;
import org.powermock.api.mockito.internal.invocation.MockitoNewInvocationControl;
import org.powermock.api.mockito.invocation.MockitoMethodInvocationControl;
import org.powermock.core.MockRepository;

/**
Expand Down
Expand Up @@ -36,7 +36,9 @@ class InvocationFactory {
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);
return Mockito.framework()
.getInvocationFactory()
.createInvocation(mock, settings, method, realMethod, arguments);
}

private Callable createRealMethod(final Object delegator, final Method method,
Expand Down
Expand Up @@ -18,8 +18,8 @@

import org.mockito.Mockito;
import org.mockito.exceptions.base.MockitoAssertionError;
import org.mockito.internal.exceptions.stacktrace.StackTraceFilter;
import org.powermock.api.mockito.internal.invocation.InvocationControlAssertionError;
import org.powermock.api.mockito.internal.stubbing.MockitoRealMethodInvocation;
import org.powermock.core.MockGateway;
import org.powermock.core.spi.MethodInvocationControl;

Expand Down Expand Up @@ -104,15 +104,7 @@ private boolean isCanBeHandledByMockito(final Method method) {
}

private boolean hasBeenCaughtByMockitoProxy() {
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
StackTraceFilter filter = new StackTraceFilter();
/*
* We filter the stack-trace to check if "Mockito" exists as a stack trace element. (The filter method
* remove all Mockito stack trace elements). If the filtered stack trace length is not equal to the original stack trace length
* this means that the call has been caught by Mockito.
*/
final StackTraceElement[] filteredStackTrace = filter.filter(stackTrace, true);
return filteredStackTrace.length != stackTrace.length;
return MockitoRealMethodInvocation.isHandledByMockito();
}

@Override
Expand All @@ -125,13 +117,6 @@ public Object reset(Object... mocks) {
throw new IllegalStateException("Internal error: No such thing as reset exists in Mockito.");
}

public Object verify(Object... mocks) {
if (mocks == null || mocks.length != 1) {
throw new IllegalArgumentException("Must supply one mock to the verify method.");
}
return Mockito.verify(mocks[0]);
}

public void verifyNoMoreInteractions() {
try {
Mockito.verifyNoMoreInteractions(getMockHandlerAdaptor().getMock());
Expand Down
Expand Up @@ -27,21 +27,22 @@ public interface PrivateMethodVerification {
*
* @throws Exception If something unexpected goes wrong.
*/
public void invoke(Object... arguments) throws Exception;
//TODO add test
void invoke(Object... arguments) throws Exception;

/**
* Verify calls to the specific method.
*
* @throws Exception If something unexpected goes wrong.
*/
public WithOrWithoutVerifiedArguments invoke(Method method) throws Exception;
WithOrWithoutVerifiedArguments invoke(Method method) throws Exception;

/**
* Verify a private method call by specifying the method name of the method
* to verify.
*
* @throws Exception If something unexpected goes wrong.
* @see {@link #invoke(Object...)}
* @see #invoke(Object...)
*/
public void invoke(String methodToVerify, Object... arguments) throws Exception;
void invoke(String methodToVerify, Object... arguments) throws Exception;
}
Expand Up @@ -18,7 +18,5 @@


public interface WithVerifiedArguments {

public abstract void withArguments(Object firstArgument, Object... additionalArguments) throws Exception;

void withArguments(Object firstArgument, Object... additionalArguments) throws Exception;
}
Expand Up @@ -17,7 +17,5 @@
package org.powermock.api.mockito.verification;

public interface WithoutVerifiedArguments {

public void withNoArguments() throws Exception;

void withNoArguments() throws Exception;
}

0 comments on commit 5faebe2

Please sign in to comment.