我写了一个工厂来产生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版本中提供

https://asolntsev.github.io/en/2020/07/11/mockito-static-methods/

dependency In your build.gradle replace mockito-core:3.3.3 by mockito-inline:3.4.0: testImplementation('org.mockito:mockito-inline:3.4.0') what are we going to mock class Buddy { static String name() { return "John"; } } Mock the static method @Test void lookMomICanMockStaticMethods() { assertThat(Buddy.name()).isEqualTo("John"); try (MockedStatic<Buddy> theMock = Mockito.mockStatic(Buddy.class)) { theMock.when(Buddy::name).thenReturn("Rafael"); assertThat(Buddy.name()).isEqualTo("Rafael"); } assertThat(Buddy.name()).isEqualTo("John"); }

我想这对我们有帮助。

其他回答

有一个简单的解决方案,使用java FunctionalInterface,然后将该接口添加为您试图进行单元测试的类的依赖项。

观察:当您在静态实体中调用静态方法时,您需要更改@PrepareForTest中的类。

例如:

securityAlgo = MessageDigest.getInstance(SECURITY_ALGORITHM);

对于上面的代码,如果您需要模拟MessageDigest类,请使用

@PrepareForTest(MessageDigest.class)

而如果你有如下内容:

public class CustomObjectRule {

    object = DatatypeConverter.printHexBinary(MessageDigest.getInstance(SECURITY_ALGORITHM)
             .digest(message.getBytes(ENCODING)));

}

然后,您需要准备这个代码所在的类。

@PrepareForTest(CustomObjectRule.class)

然后模拟这个方法:

PowerMockito.mockStatic(MessageDigest.class);
PowerMockito.when(MessageDigest.getInstance(Mockito.anyString()))
      .thenThrow(new RuntimeException());

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

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

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

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

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

对于那些使用JUnit 5的人来说,Powermock不是一个选项。您将需要以下依赖项来成功地使用Mockito模拟静态方法。

testCompile    group: 'org.mockito', name: 'mockito-core',           version: '3.6.0'
testCompile    group: 'org.mockito', name: 'mockito-junit-jupiter',  version: '3.6.0'
testCompile    group: 'org.mockito', name: 'mockito-inline',         version: '3.6.0'

mockito-junit-jupiter增加了对JUnit 5的支持。

对模拟静态方法的支持由模拟内联依赖项提供。

例子:

@Test
void returnUtilTest() {
    assertEquals("foo", UtilClass.staticMethod("foo"));

    try (MockedStatic<UtilClass> classMock = mockStatic(UtilClass.class)) {

        classMock.when(() -> UtilClass.staticMethod("foo")).thenReturn("bar");

        assertEquals("bar", UtilClass.staticMethod("foo"));
     }

     assertEquals("foo", UtilClass.staticMethod("foo"));
}

try-with-resource块用于使静态模拟保持临时状态,因此只在该范围内进行模拟。

当不使用try块时,一旦完成断言,请确保关闭mock。

MockedStatic<UtilClass> classMock = mockStatic(UtilClass.class)
classMock.when(() -> UtilClass.staticMethod("foo")).thenReturn("bar");
assertEquals("bar", UtilClass.staticMethod("foo"));
classMock.close();

模拟void方法:

在类上调用mockStatic时,该类中的所有静态void方法都会自动模拟为doNothing()。

我也遇到过类似的问题。根据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());
    }
}