From f15b5668c36f937cdf747799d8c1d21583450cbd Mon Sep 17 00:00:00 2001 From: mirkoalicastro Date: Mon, 24 Jan 2022 22:01:35 +0100 Subject: [PATCH 1/3] Fixes #2548 : Makes InOrder able to verify static methods --- src/main/java/org/mockito/InOrder.java | 35 ++++ .../org/mockito/internal/InOrderImpl.java | 17 +- .../InOrderVerificationTest.java | 191 ++++++++++++++++++ 3 files changed, 242 insertions(+), 1 deletion(-) create mode 100644 subprojects/inline/src/test/java/org/mockitoinline/InOrderVerificationTest.java diff --git a/src/main/java/org/mockito/InOrder.java b/src/main/java/org/mockito/InOrder.java index 02ea019c01..9b0202cda6 100644 --- a/src/main/java/org/mockito/InOrder.java +++ b/src/main/java/org/mockito/InOrder.java @@ -4,6 +4,8 @@ */ package org.mockito; +import static org.mockito.Mockito.times; + import org.mockito.verification.VerificationMode; /** @@ -62,6 +64,39 @@ public interface InOrder { */ T verify(T mock, VerificationMode mode); + /** + * Verifies static interaction in order, with exactly one number of invocations. + * + * @see #verify(MockedStatic, MockedStatic.Verification, VerificationMode) + */ + default void verify(MockedStatic mockedStatic, MockedStatic.Verification verification) { + verify(mockedStatic, verification, times(1)); + } + + /** + * Verifies static interaction in order. E.g: + * + *

+     * try (MockedStatic mocked = mockStatic(Foo.class)) {
+     *   InOrder inOrder = inOrder(Foo.class);
+     *
+     *   mocked.when(Foo::firstMethod).thenReturn("first");
+     *   mocked.when(Foo::secondMethod).thenReturn("second");
+     *
+     *   assertEquals("first", Foo.firstMethod());
+     *   assertEquals("second", Foo.secondMethod());
+     *
+     *   inOrder.verify(mocked, Foo::firstMethod, times(1));
+     *   inOrder.verify(mocked, Foo::secondMethod, atLeastOnce());
+     * }
+     * 
+ * + * @param mockedStatic static mock to be verified + * @param verification verification to be verified + * @param mode for example times(x) or atLeastOnce() + */ + void verify(MockedStatic mockedStatic, MockedStatic.Verification verification, VerificationMode mode); + /** * Verifies that no more interactions happened in order. * Different from {@link Mockito#verifyNoMoreInteractions(Object...)} because the order of verification matters. diff --git a/src/main/java/org/mockito/internal/InOrderImpl.java b/src/main/java/org/mockito/internal/InOrderImpl.java index af852e49ae..1001431fcc 100644 --- a/src/main/java/org/mockito/internal/InOrderImpl.java +++ b/src/main/java/org/mockito/internal/InOrderImpl.java @@ -10,6 +10,7 @@ import java.util.List; import org.mockito.InOrder; +import org.mockito.MockedStatic; import org.mockito.MockingDetails; import org.mockito.exceptions.base.MockitoException; import org.mockito.internal.verification.InOrderContextImpl; @@ -61,7 +62,8 @@ public T verify(T mock, VerificationMode mode) { } if (mode instanceof VerificationWrapper) { return mockitoCore.verify( - mock, new VerificationWrapperInOrderWrapper((VerificationWrapper) mode, this)); + mock, + new VerificationWrapperInOrderWrapper((VerificationWrapper) mode, this)); } else if (!(mode instanceof VerificationInOrderMode)) { throw new MockitoException( mode.getClass().getSimpleName() + " is not implemented to work with InOrder"); @@ -69,6 +71,19 @@ public T verify(T mock, VerificationMode mode) { return mockitoCore.verify(mock, new InOrderWrapper((VerificationInOrderMode) mode, this)); } + @Override + public void verify(MockedStatic mockedStatic, MockedStatic.Verification verification, VerificationMode mode) { + if (mode instanceof VerificationWrapper) { + mockedStatic.verify(verification, + new VerificationWrapperInOrderWrapper((VerificationWrapper) mode, this)); + } else if (mode instanceof VerificationInOrderMode) { + mockedStatic.verify(verification, new InOrderWrapper((VerificationInOrderMode) mode, this)); + } else { + throw new MockitoException( + mode.getClass().getSimpleName() + " is not implemented to work with InOrder"); + } + } + // We can't use `this.mocksToBeVerifiedInOrder.contains`, since that in turn calls `.equals` on // the mock. Since mocks can be spies and spies get their real equals method calls called, the // result is that Mockito incorrectly would register an invocation on a mock. This normally diff --git a/subprojects/inline/src/test/java/org/mockitoinline/InOrderVerificationTest.java b/subprojects/inline/src/test/java/org/mockitoinline/InOrderVerificationTest.java new file mode 100644 index 0000000000..28d2b974de --- /dev/null +++ b/subprojects/inline/src/test/java/org/mockitoinline/InOrderVerificationTest.java @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2022 Mockito contributors + * This program is made available under the terms of the MIT License. + */ +package org.mockitoinline; + +import static org.junit.Assert.fail; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.timeout; +import static org.mockito.Mockito.times; + +import java.util.function.Consumer; + +import org.junit.Test; +import org.mockito.InOrder; +import org.mockito.MockedStatic; +import org.mockito.exceptions.base.MockitoException; +import org.mockito.exceptions.misusing.NotAMockException; +import org.mockito.exceptions.verification.VerificationInOrderFailure; +import org.mockito.verification.VerificationMode; + +public class InOrderVerificationTest { + + @Test + public void shouldVerifyStaticMethods() { + mockedStaticTest(mockedStatic -> { + // given + InOrder inOrder = inOrder(StaticContext.class); + + // when + StaticContext.firstMethod(); + StaticContext.secondMethod(0); + + // then + inOrder.verify(mockedStatic, StaticContext::firstMethod); + inOrder.verify(mockedStatic, () -> StaticContext.secondMethod(0)); + }); + } + + @Test + public void shouldVerifyStaticAndInstanceMethods() { + mockedStaticTest(mockedStatic -> { + // given + StaticContext mocked = mock(StaticContext.class); + InOrder inOrder = inOrder(mocked, StaticContext.class); + + // when + StaticContext.firstMethod(); + mocked.instanceMethod(); + StaticContext.secondMethod(10); + + // then + inOrder.verify(mockedStatic, StaticContext::firstMethod); + inOrder.verify(mocked).instanceMethod(); + inOrder.verify(mockedStatic, () -> StaticContext.secondMethod(10)); + }); + } + + @Test + public void shouldVerifyStaticMethodsWithSimpleAndWrapperModes() { + mockedStaticTest(mockedStatic -> { + // given + InOrder inOrder = inOrder(StaticContext.class); + + // when + StaticContext.firstMethod(); + StaticContext.firstMethod(); + StaticContext.secondMethod(0); + + // then + inOrder.verify(mockedStatic, StaticContext::firstMethod, times(2)); + inOrder.verify(mockedStatic, () -> StaticContext.secondMethod(0), timeout(100).atLeastOnce()); + }); + } + + @Test(expected = MockitoException.class) + public void shouldThrowExceptionWhenModeIsUnsupported() { + mockedStaticTest(mockedStatic -> { + // given + VerificationMode unsupportedMode = data -> { }; + InOrder inOrder = inOrder(StaticContext.class); + + // when + StaticContext.firstMethod(); + + // then + inOrder.verify(mockedStatic, StaticContext::firstMethod, unsupportedMode); + }); + } + + @Test(expected = VerificationInOrderFailure.class) + public void shouldThrowExceptionWhenOrderIsWrong() { + mockedStaticTest(mockedStatic -> { + // given + InOrder inOrder = inOrder(StaticContext.class); + + // when + StaticContext.firstMethod(); + StaticContext.secondMethod(0); + + // then + inOrder.verify(mockedStatic, () -> StaticContext.secondMethod(0)); + inOrder.verify(mockedStatic, StaticContext::firstMethod); + }); + } + + @Test(expected = VerificationInOrderFailure.class) + public void shouldThrowExceptionWhenNoMoreInteractionsInvokedButThereAre() { + mockedStaticTest(mockedStatic -> { + // given + InOrder inOrder = inOrder(StaticContext.class); + + // when + StaticContext.firstMethod(); + StaticContext.secondMethod(0); + + // then + inOrder.verify(mockedStatic, StaticContext::firstMethod); + inOrder.verifyNoMoreInteractions(); + }); + } + + @Test(expected = VerificationInOrderFailure.class) + public void shouldThrowExceptionWhenNoMoreInteractionsInvokedWithoutVerifyingStaticMethods() { + mockedStaticTest(ignored -> { + // given + StaticContext mocked = mock(StaticContext.class); + InOrder inOrder = inOrder(StaticContext.class, mocked); + + // when + mocked.instanceMethod(); + StaticContext.firstMethod(); + + // then + inOrder.verify(mocked).instanceMethod(); + inOrder.verifyNoMoreInteractions(); + }); + } + + @Test(expected = NotAMockException.class) + public void shouldThrowExceptionWhenClassIsNotMocked() { + InOrder ignored = inOrder(StaticContext.class); + } + + @Test + public void shouldVerifyStaticMethodsWithoutInterferingWithMocking() { + mockedStaticTest(mockedStatic -> { + // given + InOrder inOrder = inOrder(StaticContext.class); + Exception expected = new RuntimeException(); + Exception actual = null; + mockedStatic.when(StaticContext::firstMethod).thenThrow(expected); + + // when + try { + StaticContext.firstMethod(); + } catch (Exception e) { + actual = e; + } + + // then + if (actual != expected) { + fail("Unable to mock static method"); + } + inOrder.verify(mockedStatic, StaticContext::firstMethod); + inOrder.verifyNoMoreInteractions(); + }); + } + + private void mockedStaticTest(Consumer> body) { + try (MockedStatic mockedStatic = mockStatic(StaticContext.class)) { + body.accept(mockedStatic); + } + } + + private static class StaticContext { + static void firstMethod() { + fail("firstMethod should be mocked"); + } + + static void secondMethod(int n) { + fail("secondMethod should be mocked but was invoked with argument " + n); + } + + void instanceMethod() { + fail("instanceMethod should be mocked"); + } + } +} From f66ab4fd30c41ce571f3e3578348eb53218accd1 Mon Sep 17 00:00:00 2001 From: mirkoalicastro Date: Wed, 16 Feb 2022 17:47:33 +0100 Subject: [PATCH 2/3] Refactors unit tests --- .../org/mockito/internal/InOrderImpl.java | 3 +- .../InOrderVerificationTest.java | 106 +++++++++++------- 2 files changed, 65 insertions(+), 44 deletions(-) diff --git a/src/main/java/org/mockito/internal/InOrderImpl.java b/src/main/java/org/mockito/internal/InOrderImpl.java index 1001431fcc..1f00976e7b 100644 --- a/src/main/java/org/mockito/internal/InOrderImpl.java +++ b/src/main/java/org/mockito/internal/InOrderImpl.java @@ -62,8 +62,7 @@ public T verify(T mock, VerificationMode mode) { } if (mode instanceof VerificationWrapper) { return mockitoCore.verify( - mock, - new VerificationWrapperInOrderWrapper((VerificationWrapper) mode, this)); + mock, new VerificationWrapperInOrderWrapper((VerificationWrapper) mode, this)); } else if (!(mode instanceof VerificationInOrderMode)) { throw new MockitoException( mode.getClass().getSimpleName() + " is not implemented to work with InOrder"); diff --git a/subprojects/inline/src/test/java/org/mockitoinline/InOrderVerificationTest.java b/subprojects/inline/src/test/java/org/mockitoinline/InOrderVerificationTest.java index 28d2b974de..fb35adc76f 100644 --- a/subprojects/inline/src/test/java/org/mockitoinline/InOrderVerificationTest.java +++ b/subprojects/inline/src/test/java/org/mockitoinline/InOrderVerificationTest.java @@ -4,6 +4,8 @@ */ package org.mockitoinline; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.Assert.fail; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; @@ -11,8 +13,7 @@ import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.times; -import java.util.function.Consumer; - +import org.assertj.core.api.Assert; import org.junit.Test; import org.mockito.InOrder; import org.mockito.MockedStatic; @@ -22,10 +23,9 @@ import org.mockito.verification.VerificationMode; public class InOrderVerificationTest { - @Test public void shouldVerifyStaticMethods() { - mockedStaticTest(mockedStatic -> { + try (MockedStatic mockedStatic = mockStatic(StaticContext.class)) { // given InOrder inOrder = inOrder(StaticContext.class); @@ -36,12 +36,12 @@ public void shouldVerifyStaticMethods() { // then inOrder.verify(mockedStatic, StaticContext::firstMethod); inOrder.verify(mockedStatic, () -> StaticContext.secondMethod(0)); - }); + } } @Test public void shouldVerifyStaticAndInstanceMethods() { - mockedStaticTest(mockedStatic -> { + try (MockedStatic mockedStatic = mockStatic(StaticContext.class)) { // given StaticContext mocked = mock(StaticContext.class); InOrder inOrder = inOrder(mocked, StaticContext.class); @@ -55,12 +55,12 @@ public void shouldVerifyStaticAndInstanceMethods() { inOrder.verify(mockedStatic, StaticContext::firstMethod); inOrder.verify(mocked).instanceMethod(); inOrder.verify(mockedStatic, () -> StaticContext.secondMethod(10)); - }); + } } @Test public void shouldVerifyStaticMethodsWithSimpleAndWrapperModes() { - mockedStaticTest(mockedStatic -> { + try (MockedStatic mockedStatic = mockStatic(StaticContext.class)) { // given InOrder inOrder = inOrder(StaticContext.class); @@ -72,12 +72,12 @@ public void shouldVerifyStaticMethodsWithSimpleAndWrapperModes() { // then inOrder.verify(mockedStatic, StaticContext::firstMethod, times(2)); inOrder.verify(mockedStatic, () -> StaticContext.secondMethod(0), timeout(100).atLeastOnce()); - }); + } } - @Test(expected = MockitoException.class) + @Test public void shouldThrowExceptionWhenModeIsUnsupported() { - mockedStaticTest(mockedStatic -> { + try (MockedStatic mockedStatic = mockStatic(StaticContext.class)) { // given VerificationMode unsupportedMode = data -> { }; InOrder inOrder = inOrder(StaticContext.class); @@ -86,13 +86,15 @@ public void shouldThrowExceptionWhenModeIsUnsupported() { StaticContext.firstMethod(); // then - inOrder.verify(mockedStatic, StaticContext::firstMethod, unsupportedMode); - }); + assertThatThrownBy(() -> + inOrder.verify(mockedStatic, StaticContext::firstMethod, unsupportedMode) + ).isInstanceOf(MockitoException.class); + } } - @Test(expected = VerificationInOrderFailure.class) + @Test public void shouldThrowExceptionWhenOrderIsWrong() { - mockedStaticTest(mockedStatic -> { + try (MockedStatic mockedStatic = mockStatic(StaticContext.class)) { // given InOrder inOrder = inOrder(StaticContext.class); @@ -101,14 +103,16 @@ public void shouldThrowExceptionWhenOrderIsWrong() { StaticContext.secondMethod(0); // then - inOrder.verify(mockedStatic, () -> StaticContext.secondMethod(0)); - inOrder.verify(mockedStatic, StaticContext::firstMethod); - }); + assertThatThrownBy(() -> { + inOrder.verify(mockedStatic, () -> StaticContext.secondMethod(0)); + inOrder.verify(mockedStatic, StaticContext::firstMethod); + }).isInstanceOf(VerificationInOrderFailure.class); + } } - @Test(expected = VerificationInOrderFailure.class) + @Test public void shouldThrowExceptionWhenNoMoreInteractionsInvokedButThereAre() { - mockedStaticTest(mockedStatic -> { + try (MockedStatic mockedStatic = mockStatic(StaticContext.class)) { // given InOrder inOrder = inOrder(StaticContext.class); @@ -118,13 +122,14 @@ public void shouldThrowExceptionWhenNoMoreInteractionsInvokedButThereAre() { // then inOrder.verify(mockedStatic, StaticContext::firstMethod); - inOrder.verifyNoMoreInteractions(); - }); + assertThatThrownBy(inOrder::verifyNoMoreInteractions) + .isInstanceOf(VerificationInOrderFailure.class); + } } - @Test(expected = VerificationInOrderFailure.class) + @Test public void shouldThrowExceptionWhenNoMoreInteractionsInvokedWithoutVerifyingStaticMethods() { - mockedStaticTest(ignored -> { + try (MockedStatic ignored = mockStatic(StaticContext.class)) { // given StaticContext mocked = mock(StaticContext.class); InOrder inOrder = inOrder(StaticContext.class, mocked); @@ -135,43 +140,60 @@ public void shouldThrowExceptionWhenNoMoreInteractionsInvokedWithoutVerifyingSta // then inOrder.verify(mocked).instanceMethod(); - inOrder.verifyNoMoreInteractions(); - }); + assertThatThrownBy(inOrder::verifyNoMoreInteractions) + .isInstanceOf(VerificationInOrderFailure.class); + } } - @Test(expected = NotAMockException.class) + @Test public void shouldThrowExceptionWhenClassIsNotMocked() { - InOrder ignored = inOrder(StaticContext.class); + assertThatThrownBy( + () -> inOrder(StaticContext.class) + ).isInstanceOf(NotAMockException.class); } @Test public void shouldVerifyStaticMethodsWithoutInterferingWithMocking() { - mockedStaticTest(mockedStatic -> { + try (MockedStatic mockedStatic = mockStatic(StaticContext.class)) { // given InOrder inOrder = inOrder(StaticContext.class); Exception expected = new RuntimeException(); - Exception actual = null; mockedStatic.when(StaticContext::firstMethod).thenThrow(expected); // when - try { - StaticContext.firstMethod(); - } catch (Exception e) { - actual = e; - } + Assert actual = assertThatThrownBy(StaticContext::firstMethod); // then - if (actual != expected) { - fail("Unable to mock static method"); - } + actual.isSameAs(expected); inOrder.verify(mockedStatic, StaticContext::firstMethod); inOrder.verifyNoMoreInteractions(); - }); + } } - private void mockedStaticTest(Consumer> body) { - try (MockedStatic mockedStatic = mockStatic(StaticContext.class)) { - body.accept(mockedStatic); + @Test + public void shouldThrowExceptionWhenVerifyUsingInOrderWithoutValidClass() { + try (MockedStatic mockedStaticContext = mockStatic(StaticContext.class)) { + try (MockedStatic mockedAnotherStaticContext = mockStatic(AnotherStaticContext.class)) { + // given + InOrder inOrder = inOrder(AnotherStaticContext.class); + + // when + mockedAnotherStaticContext.when(AnotherStaticContext::otherMethod).thenReturn("mocked value"); + StaticContext.firstMethod(); + + // then + assertThat(AnotherStaticContext.otherMethod()) + .isEqualTo("mocked value"); + inOrder.verify(mockedAnotherStaticContext, AnotherStaticContext::otherMethod); + assertThatThrownBy(() -> inOrder.verify(mockedStaticContext, StaticContext::firstMethod)) + .isInstanceOf(VerificationInOrderFailure.class); + } + } + } + + private static class AnotherStaticContext { + static String otherMethod() { + throw new AssertionError("otherMethod should be mocked"); } } From 94153ecaf066e619406931d21bcbba9412833124 Mon Sep 17 00:00:00 2001 From: mirkoalicastro Date: Wed, 16 Feb 2022 21:55:38 +0100 Subject: [PATCH 3/3] Fixes spotless violations --- src/main/java/org/mockito/InOrder.java | 5 ++++- .../java/org/mockito/internal/InOrderImpl.java | 18 ++++++++++++------ 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/mockito/InOrder.java b/src/main/java/org/mockito/InOrder.java index 9b0202cda6..3cb047a4c4 100644 --- a/src/main/java/org/mockito/InOrder.java +++ b/src/main/java/org/mockito/InOrder.java @@ -95,7 +95,10 @@ default void verify(MockedStatic mockedStatic, MockedStatic.Verification veri * @param verification verification to be verified * @param mode for example times(x) or atLeastOnce() */ - void verify(MockedStatic mockedStatic, MockedStatic.Verification verification, VerificationMode mode); + void verify( + MockedStatic mockedStatic, + MockedStatic.Verification verification, + VerificationMode mode); /** * Verifies that no more interactions happened in order. diff --git a/src/main/java/org/mockito/internal/InOrderImpl.java b/src/main/java/org/mockito/internal/InOrderImpl.java index 1f00976e7b..93f5991af0 100644 --- a/src/main/java/org/mockito/internal/InOrderImpl.java +++ b/src/main/java/org/mockito/internal/InOrderImpl.java @@ -62,7 +62,8 @@ public T verify(T mock, VerificationMode mode) { } if (mode instanceof VerificationWrapper) { return mockitoCore.verify( - mock, new VerificationWrapperInOrderWrapper((VerificationWrapper) mode, this)); + mock, + new VerificationWrapperInOrderWrapper((VerificationWrapper) mode, this)); } else if (!(mode instanceof VerificationInOrderMode)) { throw new MockitoException( mode.getClass().getSimpleName() + " is not implemented to work with InOrder"); @@ -71,15 +72,20 @@ public T verify(T mock, VerificationMode mode) { } @Override - public void verify(MockedStatic mockedStatic, MockedStatic.Verification verification, VerificationMode mode) { + public void verify( + MockedStatic mockedStatic, + MockedStatic.Verification verification, + VerificationMode mode) { if (mode instanceof VerificationWrapper) { - mockedStatic.verify(verification, - new VerificationWrapperInOrderWrapper((VerificationWrapper) mode, this)); + mockedStatic.verify( + verification, + new VerificationWrapperInOrderWrapper((VerificationWrapper) mode, this)); } else if (mode instanceof VerificationInOrderMode) { - mockedStatic.verify(verification, new InOrderWrapper((VerificationInOrderMode) mode, this)); + mockedStatic.verify( + verification, new InOrderWrapper((VerificationInOrderMode) mode, this)); } else { throw new MockitoException( - mode.getClass().getSimpleName() + " is not implemented to work with InOrder"); + mode.getClass().getSimpleName() + " is not implemented to work with InOrder"); } }