Repository: mockito/mockito
Issue: [PR open] Add feature to verify static methods calls in order

**TL;DR** Mockito doesn't allow verifying static methods calls in order. I have implemented this feature here: https://github.com/mockito/mockito/pull/2549. If possible, I'd like to contribute; or discuss other ways to achieve the same.

---

Hey Mockito team.

This discussion relates to: https://groups.google.com/g/mockito/c/wjd9jIj_oBs. Since I'm not sure that the Google group is active, I'm posting it here for now.

This issue is about verifying static methods calls in order. I'm aware of the InOrder class, but we can't pass a MockedStatic instance. I had a closer look, and we can pass to it a Class instance. But the InOrder interface is not powerful enough to verify that a static method has been called:
```
public interface InOrder {
  <T> T verify(T mock);
  <T> T verify(T mock, VerificationMode mode);
  void verifyNoMoreInteractions();
}
```

To verify static calls, using method references and lambdas, we likely need two more methods in the InOrder interface, with signatures similar to MockedStatic#verify:
```
public interface MockedStatic<T> extends ScopedMock {
  void verify(Verification verification)
  void verify(Verification verification, VerificationMode mode);
  ...
}
```

What do you think of the InOrder interface exposing two methods to deal with the MockedStatic objects that accept both the MockedStatic and the Verification instances to use? Like the following:
```
void verify(MockedStatic<?> mockedStatic, MockedStatic.Verification verification);
void verify(MockedStatic<?> mockedStatic, MockedStatic.Verification verification, VerificationMode mode);
```

I made the above changes, and we can verify static methods, together with instance methods too. I have updated the tests as well in my PR.

Let me know if you are aware of other ways to achieve the same. If it is not doable, then please share your thoughts about the above, and if I can contribute in case.

Issue comments:
As a Mockito user I would love to have this new feature @mirkoalicastro 
> As a Mockito user I would love to have this new feature @mirkoalicastro

I made it more clear that there is already a possible implementation with the mentioned PR, which includes tests as well, of course. I hope someone will look into this.

@TimvdLippe Are you able to provide some feedback on this / to involve relevant people? Many thanks.
Sorry I hadn't gotten to it. I responded to the PR, it's looking good! We mainly need to figure out how we are handling the `Class` that is matching the `mockedStatic`, but I think in that case we should be passing that into the `inOrder()` method call. Let's iterate on the PR and see where it goes!

Pull request review comments:
Nit: for readability, I would prefer this to not be inlined. E.g. let's put this try-catch in each of the methods to make it clear where the `MockedStatic` is coming from. (This is mostly for test code, as for production code this would have been a fine way to solve it)
Nit: it's easier to use `org.assertj.core.api.Assertions.assertThatThrownBy` here, as it makes sure we don't accidentally miss verifying the exception was thrown.
Nit: do not use `@Test(expected)` and instead use `org.assertj.core.api.Assertions.assertThatThrownBy`. We recently migrated away from that in #2510 (I should probably check if we can enable ErrorProne to error on this).
Nice tests! Can we also have a test where we invoke `inOrder` with a different class? E.g. I would expect the following use case to be illegal:

```java
// Declaring `Bar` here rather than `Foo`
InOrder inOrder = inOrder(Bar.class);

mocked.when(Foo::firstMethod).thenReturn("first");
mocked.when(Foo::secondMethod).thenReturn("second");

assertEquals("first", Foo.firstMethod());
assertEquals("second", Foo.secondMethod());

// Execution verification on `Foo`, while `inOrder` was created with `Bar`
inOrder.verify(mocked, Foo::firstMethod, times(1));
inOrder.verify(mocked, Foo::secondMethod, atLeastOnce());
```
Definitely, it looks a great test to add. Done! 
Thanks for the suggestion, I have used `assertThatThrownBy` in all the tests.
Sure, I see your point. I have inlined that in the tests.

Patch that resolved the issue:
diff --git a/src/main/java/org/mockito/InOrder.java b/src/main/java/org/mockito/InOrder.java
index 02ea019c01..3cb047a4c4 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,42 @@ public interface InOrder {
      */
     <T> 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:
+     *
+     * <pre class="code"><code class="java">
+     * try (MockedStatic<Foo> 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());
+     * }
+     * </code></pre>
+     *
+     * @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 <b>in order</b>.
      * 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..93f5991af0 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> 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,24 @@ public <T> 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


Test patch:
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..fb35adc76f
--- /dev/null
+++ b/subprojects/inline/src/test/java/org/mockitoinline/InOrderVerificationTest.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright (c) 2022 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+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;
+import static org.mockito.Mockito.mockStatic;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.times;
+
+import org.assertj.core.api.Assert;
+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() {
+        try (MockedStatic<StaticContext> mockedStatic = mockStatic(StaticContext.class)) {
+            // 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() {
+        try (MockedStatic<StaticContext> mockedStatic = mockStatic(StaticContext.class)) {
+            // 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() {
+        try (MockedStatic<StaticContext> mockedStatic = mockStatic(StaticContext.class)) {
+            // 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
+    public void shouldThrowExceptionWhenModeIsUnsupported() {
+        try (MockedStatic<StaticContext> mockedStatic = mockStatic(StaticContext.class)) {
+            // given
+            VerificationMode unsupportedMode = data -> { };
+            InOrder inOrder = inOrder(StaticContext.class);
+
+            // when
+            StaticContext.firstMethod();
+
+            // then
+            assertThatThrownBy(() ->
+                inOrder.verify(mockedStatic, StaticContext::firstMethod, unsupportedMode)
+            ).isInstanceOf(MockitoException.class);
+        }
+    }
+
+    @Test
+    public void shouldThrowExceptionWhenOrderIsWrong() {
+        try (MockedStatic<StaticContext> mockedStatic = mockStatic(StaticContext.class)) {
+            // given
+            InOrder inOrder = inOrder(StaticContext.class);
+
+            // when
+            StaticContext.firstMethod();
+            StaticContext.secondMethod(0);
+
+            // then
+            assertThatThrownBy(() -> {
+                inOrder.verify(mockedStatic, () -> StaticContext.secondMethod(0));
+                inOrder.verify(mockedStatic, StaticContext::firstMethod);
+            }).isInstanceOf(VerificationInOrderFailure.class);
+        }
+    }
+
+    @Test
+    public void shouldThrowExceptionWhenNoMoreInteractionsInvokedButThereAre() {
+        try (MockedStatic<StaticContext> mockedStatic = mockStatic(StaticContext.class)) {
+            // given
+            InOrder inOrder = inOrder(StaticContext.class);
+
+            // when
+            StaticContext.firstMethod();
+            StaticContext.secondMethod(0);
+
+            // then
+            inOrder.verify(mockedStatic, StaticContext::firstMethod);
+            assertThatThrownBy(inOrder::verifyNoMoreInteractions)
+                .isInstanceOf(VerificationInOrderFailure.class);
+        }
+    }
+
+    @Test
+    public void shouldThrowExceptionWhenNoMoreInteractionsInvokedWithoutVerifyingStaticMethods() {
+        try (MockedStatic<StaticContext> ignored = mockStatic(StaticContext.class)) {
+            // given
+            StaticContext mocked = mock(StaticContext.class);
+            InOrder inOrder = inOrder(StaticContext.class, mocked);
+
+            // when
+            mocked.instanceMethod();
+            StaticContext.firstMethod();
+
+            // then
+            inOrder.verify(mocked).instanceMethod();
+            assertThatThrownBy(inOrder::verifyNoMoreInteractions)
+                .isInstanceOf(VerificationInOrderFailure.class);
+        }
+    }
+
+    @Test
+    public void shouldThrowExceptionWhenClassIsNotMocked() {
+        assertThatThrownBy(
+            () -> inOrder(StaticContext.class)
+        ).isInstanceOf(NotAMockException.class);
+    }
+
+    @Test
+    public void shouldVerifyStaticMethodsWithoutInterferingWithMocking() {
+        try (MockedStatic<StaticContext> mockedStatic = mockStatic(StaticContext.class)) {
+            // given
+            InOrder inOrder = inOrder(StaticContext.class);
+            Exception expected = new RuntimeException();
+            mockedStatic.when(StaticContext::firstMethod).thenThrow(expected);
+
+            // when
+            Assert<?, ?> actual = assertThatThrownBy(StaticContext::firstMethod);
+
+            // then
+            actual.isSameAs(expected);
+            inOrder.verify(mockedStatic, StaticContext::firstMethod);
+            inOrder.verifyNoMoreInteractions();
+        }
+    }
+
+    @Test
+    public void shouldThrowExceptionWhenVerifyUsingInOrderWithoutValidClass() {
+        try (MockedStatic<StaticContext> mockedStaticContext = mockStatic(StaticContext.class)) {
+            try (MockedStatic<AnotherStaticContext> 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");
+        }
+    }
+
+    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");
+        }
+    }
+}