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

26,932

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);
    }
}
Share:
26,932
Glory to Russia
Author by

Glory to Russia

Updated on July 05, 2022

Comments

  • Glory to Russia
    Glory to Russia almost 2 years

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

    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 open method a certain SQL statement (DROP TABLE IF EXISTS PERSON) is executed.

    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, DriverManager returns real connection.

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

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

    <?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>
    
  • Glory to Russia
    Glory to Russia over 10 years
    I've done this before (see altruix.wordpress.com/portfolio/project-control-center for details). The problem is that if you write lots of classes you end up with lots of boilerplate code (interface, factory interfaces and their implementations). So I tried to get the advantage of that design (testability) without the costs (lot of boilerplate code).
  • Tom Anderson
    Tom Anderson over 10 years
    That is definitely a danger of taking this approach when writing mock-based tests. I work on a codebase where much of the logic has been ground to a fine powder by the millstones of mock-driven refactoring. In this case, given that DataSource already exists, represents a well-defined, self-contained concept, and has implementations already, i would think that the risk of boilerplate overgrowth is small.