diff --git a/src/main/java/org/mockito/internal/handler/MockHandlerImpl.java b/src/main/java/org/mockito/internal/handler/MockHandlerImpl.java index 17b967efcf..e58659a166 100644 --- a/src/main/java/org/mockito/internal/handler/MockHandlerImpl.java +++ b/src/main/java/org/mockito/internal/handler/MockHandlerImpl.java @@ -107,18 +107,25 @@ public Object handle(Invocation invocation) throws Throwable { mockingProgress().reportOngoingStubbing(ongoingStubbing); } } else { - Object ret = mockSettings.getDefaultAnswer().answer(invocation); - DefaultAnswerValidator.validateReturnValueFor(invocation, ret); - - // Mockito uses it to redo setting invocation for potential stubbing in case of partial - // mocks / spies. - // Without it, the real method inside 'when' might have delegated to other self method - // and overwrite the intended stubbed method with a different one. - // This means we would be stubbing a wrong method. - // Typically this would led to runtime exception that validates return type with stubbed - // method signature. - invocationContainer.resetInvocationForPotentialStubbing(invocationMatcher); - return ret; + try { + Object ret = mockSettings.getDefaultAnswer().answer(invocation); + DefaultAnswerValidator.validateReturnValueFor(invocation, ret); + + return ret; + } finally { + // Mockito uses it to redo setting invocation for potential stubbing in case of + // partial + // mocks / spies. + // Without it, the real method inside 'when' might have delegated to other self + // method + // and overwrite the intended stubbed method with a different one. + // This means we would be stubbing a wrong method. + // Typically this would led to runtime exception that validates return type with + // stubbed + // method signature. + invocationContainer.resetInvocationForPotentialStubbing(invocationMatcher); + mockingProgress().reportOngoingStubbing(ongoingStubbing); + } } } diff --git a/subprojects/inline/src/test/java/org/mockitoinline/bugs/OngoingStubShiftTest.java b/subprojects/inline/src/test/java/org/mockitoinline/bugs/OngoingStubShiftTest.java new file mode 100644 index 0000000000..aa6fdbe10f --- /dev/null +++ b/subprojects/inline/src/test/java/org/mockitoinline/bugs/OngoingStubShiftTest.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2007 Mockito contributors + * This program is made available under the terms of the MIT License. + */ +package org.mockitoinline.bugs; + +import static org.junit.Assert.assertEquals; +import org.junit.Test; +import org.mockito.MockedStatic; +import static org.mockito.Mockito.CALLS_REAL_METHODS; +import static org.mockito.Mockito.mockStatic; + +public class OngoingStubShiftTest { + + private static class StaticInt { + static int getInt() { + return 1; + } + } + + private static class StaticStr { + static String getStr() { + return Integer.toString(StaticInt.getInt()); + } + } + + @Test + public void keep_ongoing_stub_when_spy() { + try (MockedStatic mockInt = mockStatic(StaticInt.class); + MockedStatic mockStr = mockStatic(StaticStr.class, CALLS_REAL_METHODS)) { + + mockStr.when(StaticStr::getStr).thenReturn("1"); + assertEquals("1", StaticStr.getStr()); + } + } + + private static class StaticWithException { + static int outer() { + return inner() + 1; + } + + static int inner() { + throw new NullPointerException(); + } + } + + @Test + public void keep_ongoing_stub_when_exception() { + try (MockedStatic mock = mockStatic(StaticWithException.class, CALLS_REAL_METHODS)) { + mock.when(StaticWithException::outer).thenReturn(1); + assertEquals(1, StaticWithException.outer()); + } + } +}