2013-10-19 3 views
6

Ich habe eine Klasse, die eine Verbindung zu einer H2-Datenbank herstellt und mehrere SQL-Anweisungen ausführt.Wie man DriverManager.getConnection (...) mockt?

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

Ich möchte einen Komponententest schreiben, der ausgeführt wird, überprüft, dass in dem Verfahren eine bestimmte open SQL-Anweisung (DROP TABLE IF EXISTS PERSON).

Um dies zu tun, ich schrieb Test folgende:

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")); 
    } 
} 

Aber es funktioniert nicht - statt der Mock-Verbindung, DriverManager kehrt wirkliche Verbindung.

Wie kann ich es beheben (make DriverManager Rückverbindung Pseudo im Test)?

Hier ist die pom.xml meines Projekts, vielleicht ist etwas nicht in Ordnung. eine Instanz, die in die Klasse in Frage

<?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> 

Antwort

6

Dies funktioniert (achten Sie auf die Importe):

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); 
    } 
} 
0

Der üblicher Weg, dies zu tun wäre, um die Verbindung Schöpfung in einer anderen Klasse ausklammern und injiziert. Sie können dann diese neue Klasse vortäuschen.

In Ihrem Fall so etwas wie diese:

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 diesem speziellen Fall, noch besser wäre wahrscheinlich die Standard-JDBC-Schnittstelle DataSource statt Ihrer eigenen Verbindungs-Factory-Klasse zu verwenden sein:

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(); 
     } 
    } 
} 
+0

Ich habe dies vor getan e (siehe http://altruix.wordpress.com/portfolio/project-control-center/ für Details). Das Problem ist, dass, wenn Sie viele Klassen schreiben, Sie mit viel Code (Interface, Factory Interfaces und deren Implementierungen) enden. Also habe ich versucht, den Vorteil dieses Designs (Testbarkeit) ohne die Kosten (viel Code) zu bekommen. –

+0

Das ist definitiv eine Gefahr, diesen Ansatz beim Schreiben von Scheintests zu verwenden. Ich arbeite an einer Codebasis, bei der ein Großteil der Logik durch die Mühlsteine ​​einer nachgebildeten Umgestaltung zu einem feinen Pulver gemahlen wurde. In diesem Fall, vorausgesetzt, dass "DataSource" bereits existiert, ein wohldefiniertes, in sich geschlossenes Konzept darstellt und Implementierungen bereits aufweist, würde ich denken, dass das Risiko des Übergreifens von Boilerplatten gering ist. –

Verwandte Themen