我写了一个工厂来产生java.sql.Connection对象:

public class MySQLDatabaseConnectionFactory implements DatabaseConnectionFactory {

    @Override public Connection getConnection() {
        try {
            return DriverManager.getConnection(...);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
}

我想验证传递给DriverManager的参数。getConnection,但我不知道如何模拟静态方法。我的测试用例使用JUnit 4和Mockito。是否有一个好的方法来模拟/验证这个特定的用例?


当前回答

从Mockito 3.4.0开始,就可以在Mockito中模拟静态方法。 详情见:

https://github.com/mockito/mockito/releases/tag/v3.4.0

https://github.com/mockito/mockito/issues/1013

https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html#static_mocks

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());

在你的例子中,是这样的:

  @Test
  public void testStaticMockWithVerification() throws SQLException {
    try (MockedStatic<DriverManager> dummy = Mockito.mockStatic(DriverManager.class)) {
      DatabaseConnectionFactory factory = new MySQLDatabaseConnectionFactory();
      dummy.when(() -> DriverManager.getConnection("arg1", "arg2", "arg3"))
        .thenReturn(new Connection() {/*...*/});

      factory.getConnection();

      dummy.verify(() -> DriverManager.getConnection(eq("arg1"), eq("arg2"), eq("arg3")));
    }
  }

注意:模拟STATIC METHODS需要模拟内联依赖而不是模拟核心。

对于JUnit5也添加这个:

<dependency>
  <groupId>org.mockito</groupId>
  <artifactId>mockito-junit-jupiter</artifactId>
  <version>${mockito.version}</version>
  <scope>test</scope>
</dependency>

其他回答

对于模仿静态函数,我能够这样做:

在helper类/对象中创建一个包装器函数。(使用名称变体可能有助于保持内容的分离和可维护。) 在代码中使用此包装器。(是的,代码的实现需要考虑到测试。) 模拟包装器函数。

包装器代码片段(不是真正的功能,只是为了说明)

class myWrapperClass ...
    def myWrapperFunction (...) {
        return theOriginalFunction (...)
    }

当然,在一个包装器类中积累多个这样的函数可能有利于代码重用。

我也遇到过类似的问题。根据PowerMock的mockStatic文档,接受的答案对我不起作用,直到我做出了更改:@PrepareForTest(classthatcontainsstaticmethod .class)。

我不需要使用BDDMockito。

我的类:

public class SmokeRouteBuilder {
    public static String smokeMessageId() {
        try {
            return InetAddress.getLocalHost().getHostAddress();
        } catch (UnknownHostException e) {
            log.error("Exception occurred while fetching localhost address", e);
            return UUID.randomUUID().toString();
        }
    }
}

我的测试类:

@RunWith(PowerMockRunner.class)
@PrepareForTest(SmokeRouteBuilder.class)
public class SmokeRouteBuilderTest {
    @Test
    public void testSmokeMessageId_exception() throws UnknownHostException {
        UUID id = UUID.randomUUID();

        mockStatic(InetAddress.class);
        mockStatic(UUID.class);
        when(InetAddress.getLocalHost()).thenThrow(UnknownHostException.class);
        when(UUID.randomUUID()).thenReturn(id);

        assertEquals(id.toString(), SmokeRouteBuilder.smokeMessageId());
    }
}

由于该方法是静态的,它已经具备了使用它所需的一切,因此它违背了模拟的目的。 模仿静态方法被认为是一种糟糕的实践。

如果您尝试这样做,这意味着您想要执行测试的方式有问题。

当然,您可以使用PowerMockito或任何其他能够做到这一点的框架,但请重新考虑您的方法。

例如:尝试模拟/提供对象,而静态方法使用这些对象。

从Mockito 3.4.0开始,就可以在Mockito中模拟静态方法。 详情见:

https://github.com/mockito/mockito/releases/tag/v3.4.0

https://github.com/mockito/mockito/issues/1013

https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html#static_mocks

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());

在你的例子中,是这样的:

  @Test
  public void testStaticMockWithVerification() throws SQLException {
    try (MockedStatic<DriverManager> dummy = Mockito.mockStatic(DriverManager.class)) {
      DatabaseConnectionFactory factory = new MySQLDatabaseConnectionFactory();
      dummy.when(() -> DriverManager.getConnection("arg1", "arg2", "arg3"))
        .thenReturn(new Connection() {/*...*/});

      factory.getConnection();

      dummy.verify(() -> DriverManager.getConnection(eq("arg1"), eq("arg2"), eq("arg3")));
    }
  }

注意:模拟STATIC METHODS需要模拟内联依赖而不是模拟核心。

对于JUnit5也添加这个:

<dependency>
  <groupId>org.mockito</groupId>
  <artifactId>mockito-junit-jupiter</artifactId>
  <version>${mockito.version}</version>
  <scope>test</scope>
</dependency>

要模拟静态方法,您应该使用Powermock查看: https://github.com/powermock/powermock/wiki/MockStatic。 Mockito不提供这种功能。

你可以读到一篇关于mockito的文章: http://refcardz.dzone.com/refcardz/mockito