IMLC.ME

Mockito 如何 mock 静态方法

自 3.4.0,Mockito 提供了静态方法的 mocking 支持。

首先,你需要引入 mockito-inline 依赖(或者把 mockito-core 改成 mockito-inline)。

<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-inline</artifactId>
    <version>4.3.1</version>
    <scope>test</scope>
</dependency>

这里千万要注意使用 try-with-resource,MockedStatic 使用完之后需要关闭。
MockedStatic 继承了 AutoCloseable,可以通过 try-with-resource 自动关闭。

MockedStatic.when(...) 方法接收一个方法引用作为入参。如果是无参数的静态方法,可以直接用 Foo::method 的形式传递。

MockedStatic 关闭之后,mock 就会被恢复原状。

assertEquals("foo", Foo.method());
try (MockedStatic mocked = mockStatic(Foo.class)) {
    mocked.when(Foo::method).thenReturn("bar");
    assertEquals("bar", Foo.method());
    mocked.verify(Foo::method);
}
assertEquals("foo", Foo.method());

代码基于 junit5 编写。

1. Mock 无参数的静态方法

@Test
void mockStaticMethodWithoutArgument() throws InterruptedException {
    try (MockedStatic<LocalDateTime> mockedStatic = mockStatic(LocalDateTime.class)) {
      mockedStatic.when(
              () -> {
                LocalDateTime.now();
              })
          .thenReturn(LocalDateTime.MIN);
    
      assertEquals(LocalDateTime.now(), LocalDateTime.MIN);
    }
}

2. Mock 有参数的静态方法

@Test
void mockStaticMethodWithArguments() throws InterruptedException {
  try (MockedStatic<LocalDateTime> mockedStatic = mockStatic(LocalDateTime.class)) {
        mockedStatic.when(
          () -> {
            LocalDateTime.parse("anyString");
        })
        .thenReturn(LocalDateTime.MIN);
        
        assertEquals(LocalDateTime.parse("anyString"), LocalDateTime.MIN);
    }
}

3. 使用 ArgumentMatcher

同样的,按照 Mockito 一贯的风格,我们同样可以使用 ArgumentMatcher 。

@Test
void mockStaticMethodWithArgumentsUsingArgumentMatcher() throws InterruptedException {
    try (MockedStatic<LocalDateTime> mockedStatic = mockStatic(LocalDateTime.class)) {
      mockedStatic.when(
              () -> {
                LocalDateTime.parse(anyString());
              })
          .thenReturn(LocalDateTime.MIN);
    
      assertEquals(LocalDateTime.parse("anyString"), LocalDateTime.MIN);
      assertEquals(LocalDateTime.parse("otherString"), LocalDateTime.MIN);
    }
}

4. 校验静态方法调用

校验方法调用跟普通的 mocking 差不多。

mockedStatic.verify(() -> {LocalDateTime.now();});
mockedStatic.verify(() -> {LocalDateTime.now();}, times(2));

5. 获取实参

要获得静态方法调用时传入的参数,一样是老一套的 ArgumentCaptor。

ArgumentCaptor<String> argumentCaptor = ArgumentCaptor.forClass(String.class);
    try (MockedStatic<LocalDateTime> mockedStatic = mockStatic(LocalDateTime.class)) {
    mockedStatic.when(
    () -> {
        LocalDateTime.parse(argumentCaptor.capture());
    })
    .thenReturn(LocalDateTime.MIN);

    LocalDateTime.parse("Hello, world!");

    assertEquals("Hello, world!", argumentCaptor.getValue());
}

参考文献

48. Mocking static methods (since 3.4.0)