Java 使用 PowerMock 和 Mockito 模拟静态方法

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/21128690/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 06:47:21  来源:igfitidea点击:

Mocking static methods with PowerMock and Mockito

javaunit-testingjunitmockitopowermock

提问by Naftuli Kay

I'm trying to verify a call to java.sql.DriverManager.getConnectionusing JUnit, Mockito, and PowerMock.

我正在尝试验证对java.sql.DriverManager.getConnection使用 JUnit、Mockito 和 PowerMock的调用。

Here's my test case:

这是我的测试用例:

@RunWith(PowerMockRunner.class)
@PrepareForTest(DriverManager.class)
public class MySQLDatabaseConnectionFactoryTest {

    private ConfigurationService configurationService;
    private MySQLDatabaseConnectionFactory reference;

    @Before
    public void setUp() throws Exception {
        this.reference = new MySQLDatabaseConnectionFactory();
    }

    @Test
    public void testGetConnection() throws SQLException {
//      setup
        Connection connection = mock(Connection.class);

        PowerMockito.mockStatic(DriverManager.class);

        when(DriverManager.getConnection(anyString(), anyString(), anyString())).thenReturn(connection);

//      run
        this.reference.getConnection();

//      verify
        PowerMockito.verifyStatic();
        DriverManager.getConnection("jdbc:mysql://myhost:1111/database", "username", "password");
    }

}

Here's the code under test:

下面是测试中的代码:

public class MySQLDatabaseConnectionFactory implements
        DatabaseConnectionFactory {

    @Override
    public Connection getConnection(IApplicationInstance appInstance) {         
        try {
            return DriverManager.getConnection(String.format("jdbc:mysql://%s:%d/%s", 
                    MYSQL_HOST, MYSQL_PORT, MYSQL_DATABASE), MYSQL_USERNAME, MYSQL_PASSWORD);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
}

Interestingly enough, this code fails with a java.sql.SQLException:

有趣的是,这段代码失败了java.sql.SQLException

java.lang.RuntimeException: java.sql.SQLException: No suitable driver found for jdbc:mysql://myhost:1111/database

Now, I could easily just make sure that my SQL driver (MySQL in this case) is loaded at test time, but why isn't the static method completely mocked out without side-effects?

现在,我可以轻松地确保在测试时加载了我的 SQL 驱动程序(在本例中为 MySQL),但是为什么没有完全模拟静态方法而没有副作用?

Update:

更新:

I've better isolated the problem. I've added a test method to my test case which tries getting a connection from DriverManager:

我最好把问题隔离开来。我在我的测试用例中添加了一个测试方法,它尝试从DriverManager以下位置获取连接:

@Test
public void testSomething() {
    Connection conn = mock(Connection.class);
    mockStatic(DriverManager.class);
    when(DriverManager.getConnection(anyString())).thenReturn(conn);
    Connection c = DriverManager.getConnection("whut");
    verifyStatic();
    DriverManager.getConnection("whut");
}

This test actually passes, while the other test still fails. It seems that PowerMock isn't mocking the reference to the class inside of MySQLDatabaseConnectionFactory. How can I work around this?

这个测试实际上通过了,而另一个测试仍然失败。似乎 PowerMock 并没有嘲笑对MySQLDatabaseConnectionFactory. 我该如何解决这个问题?

采纳答案by MariuszS

Changing your @PrepareForTestannotation value to MySQLDatabaseConnectionFactory.classwill resolve this issue.

将您的@PrepareForTest注释值更改为MySQLDatabaseConnectionFactory.class将解决此问题。

This annotation tells PowerMock to prepare certain classes for testing. Classes needed to be defined using this annotation are typically those that needs to be byte-code manipulated. This includes final classes, classes with final, private, static.

这个注解告诉 PowerMock 为测试准备某些类。需要使用此注释定义的类通常是那些需要进行字节码操作的类。这包括 final 类,具有 final、private 和 static 的类。

In this situation PowerMockito have to replace invocation to static method DriverManager.getConnectionwith mocked code. This is done with usage of byte-code manipulation.

在这种情况下,PowerMockito 必须DriverManager.getConnection用模拟代码替换对静态方法的调用。这是通过使用字节码操作来完成的。

Full code

完整代码

@RunWith(PowerMockRunner.class)
@PrepareForTest(MySQLDatabaseConnectionFactory.class)
public class MySQLDatabaseConnectionFactoryTest {

    private MySQLDatabaseConnectionFactory reference;

    @Before
    public void setUp() throws Exception {
        reference = new MySQLDatabaseConnectionFactory();
    }

    @Test
    public void testGetConnection() throws SQLException {

        // given
        PowerMockito.mockStatic(DriverManager.class);
        BDDMockito.given(DriverManager.getConnection(anyString(), anyString(), anyString()))
             .willReturn(mock(Connection.class));

        // when
        reference.getConnection();

        // then
        PowerMockito.verifyStatic();
        DriverManager.getConnection("jdbc:mysql://myhost:1111/database", "username", "password");
    }
}

Thanks to @Szpakfor helping me to resolve this issue!

感谢@Szpak帮助我解决了这个问题!