Java 如何模拟 DriverManager.getConnection(...)?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/19464975/
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-12 17:30:12  来源:igfitidea点击:

How to mock DriverManager.getConnection(...)?

javaunit-testingmockingmockitopowermock

提问by Dmitrii Pisarenko

I have a class, which connects to an H2 database and runs several SQL statements.

我有一个类,它连接到 H2 数据库并运行多个 SQL 语句。

public class H2Persistence implements IPersistence {

    private Connection conn;

    @Override
    public void open() {
        try
        {
            Class.forName("org.h2.Driver");
            conn = DriverManager.getConnection(CONN_TYPE_USER_HOME);

            final Statement stmt = conn.createStatement();

            stmt.executeUpdate("CREATE TABLE PERSON(" +
                    "ID BIGINT,"+
                    "AGEGROUP VARCHAR(255),"+
                    "MONTHLY_INCOME_LEVEL VARCHAR(255)," +
                    "GENDER VARCHAR(1),"+
                    "HOUSEHOLD_ID BIGINT)");

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
...
}

I want to write a unit test, which verifies, that in the openmethod a certain SQL statement (DROP TABLE IF EXISTS PERSON) is executed.

我想编写一个单元测试,它验证在该open方法中DROP TABLE IF EXISTS PERSON是否执行了某个 SQL 语句 ( )。

In order to do this, I wrote following test:

为了做到这一点,我编写了以下测试:

import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.powermock.api.mockito.PowerMockito.mockStatic;
import static org.powermock.api.mockito.PowerMockito.when;


@RunWith(PowerMockRunner.class)
@PrepareForTest(DriverManager.class)
public class H2PersistenceTest {
    @Test
    public void testDropPersonIsCalled() throws SQLException {
        final Statement statement = mock(Statement.class);

        final Connection connection = mock(Connection.class);

        when(connection.createStatement()).thenReturn(statement);

        mockStatic(DriverManager.class);

        when(DriverManager.getConnection(H2Persistence.CONN_TYPE_USER_HOME)).thenReturn
                (connection);


        final H2Persistence objectUnderTest = new H2Persistence();

        objectUnderTest.open();
        verify(statement.executeUpdate("DROP TABLE IF EXISTS PERSON"));
    }
}

But it doesn't work - instead of the mock connection, DriverManagerreturns real connection.

但它不起作用 - 而不是模拟连接,DriverManager返回真实连接。

How can I fix it (make DriverManagerreturn connection mock in the test) ?

我该如何修复它(DriverManager在测试中模拟返回连接)?

Here's the pom.xmlof my project, maybe something is wrong there.

pom.xml是我的项目,也许那里有问题。

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>ru.mycompany</groupId>
    <artifactId>myproduct</artifactId>
    <version>1.0-SNAPSHOT</version>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <powermock.version>1.5.1</powermock.version>
        <maven.compiler.source>1.6</maven.compiler.source>
        <maven.compiler.target>1.6</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.10</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.easytesting</groupId>
            <artifactId>fest-util</artifactId>
            <version>1.2.3</version>
        </dependency>
        <dependency>
            <groupId>org.easytesting</groupId>
            <artifactId>fest-assert-core</artifactId>
            <version>2.0M8</version>
        </dependency>
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>15.0</version>
        </dependency>
        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-all</artifactId>
            <version>1.9.5</version>
        </dependency>
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <version>1.3.173</version>
        </dependency>
        <dependency>
            <groupId>org.powermock</groupId>
            <artifactId>powermock-module-junit4</artifactId>
            <version>${powermock.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.powermock</groupId>
            <artifactId>powermock-api-mockito</artifactId>
            <version>${powermock.version}</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

</project>

采纳答案by Dmitrii Pisarenko

This one works (pay attention to the imports):

这个有效(注意进口):

import static org.easymock.EasyMock.expect;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.powermock.api.easymock.PowerMock.mockStatic;
import static org.powermock.api.easymock.PowerMock.replay;


@RunWith(PowerMockRunner.class)
@PrepareForTest({DriverManager.class, H2Persistence.class})
public class H2PersistenceTest {
    @Test
    public void testDropPersonIsCalled() throws SQLException {
        final Statement statement = mock(Statement.class);

        final Connection connection = mock(Connection.class);

        when(connection.createStatement()).thenReturn(statement);

        mockStatic(DriverManager.class);

        expect(DriverManager.getConnection(H2Persistence.CONN_TYPE_USER_HOME))
                .andReturn(connection);
        expect(DriverManager.getConnection(null))
                .andReturn(null);

        replay(DriverManager.class);
        final H2Persistence objectUnderTest = new H2Persistence();

        objectUnderTest.open();

        verify(statement).executeUpdate("DROP TABLE IF EXISTS PERSON");
        verify(statement).executeUpdate(H2Persistence.CREATE_TABLE_PERSON);
    }
}

回答by Tom Anderson

The usual way to do this would be to factor out the connection creation into another class, and inject an instance of that into the class in question. You can then mock that new class.

执行此操作的常用方法是将连接创建分解到另一个类中,并将其实例注入相关类中。然后你可以模拟那个新类。

In your case, something like this:

在你的情况下,是这样的:

public class H2Persistence implements IPersistence {
    private final ConnectionFactory connectionFactory;
    private Connection conn;

    public H2Persistence(ConnectionFactory connectionFactory) {
        this.connectionFactory = connectionFactory;
    }

    @Override
    public void open() {
        try {
            conn = connectionFactory.createConnection(CONN_TYPE_USER_HOME);
            // etc
        }
        catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

public class ConnectionFactory {

    Connection createConnection(String connType) throws SQLException, ClassNotFoundException {
        Class.forName("org.h2.Driver");
        return DriverManager.getConnection(connType);
    }

}

In this particular case, even better would probably be to use the standard JDBC interface DataSourceinstead of your own connection factory class:

在这种特殊情况下,最好使用标准 JDBC 接口DataSource而不是您自己的连接工厂类:

public class H2Persistence implements IPersistence {
    private final DataSource dataSource;
    private Connection conn;

    public H2Persistence(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    @Override
    public void open() {
        try {
            conn = dataSource.getConnection();
            // etc
        }
        catch (SQLException e) {
            e.printStackTrace();
        }
    }
}