2013-10-19 15 views
6

Mam klasę, która łączy się z bazą danych H2 i uruchamia kilka instrukcji SQL.Jak kpić DriverManager.getConnection (...)?

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

Chcę napisać badanej jednostki, która sprawdza, że ​​w sposobie open pewna instrukcja SQL (DROP TABLE IF EXISTS PERSON) jest wykonywany.

W tym celu napisałem następujący 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")); 
    } 
} 

Ale to nie działa - zamiast udawanym związku, DriverManager zwrotów prawdziwego związku.

Jak mogę to naprawić (sprawić, że w trakcie testu zostanie przywrócone połączenie zwrotne DriverManager)?

Oto pom.xml z mojego projektu, może coś jest nie tak.

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

Odpowiedz

6

Ten działa (zwrócić uwagę na import):

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

Zwykłym sposobem na to byłoby czynnikiem na utworzenie połączenia do innej klasy i wstrzyknąć instancji, że do danej klasy. Możesz wtedy kpić z tej nowej klasy.

W twoim przypadku, coś takiego:

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

} 

W tym konkretnym przypadku, nawet lepiej byłoby prawdopodobnie użyć standardowego interfejsu JDBC DataSource zamiast swojej własnej klasy fabrycznej gra:

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

robiłem to befor e (więcej informacji na stronie http://altruix.wordpress.com/portfolio/project-control-center/). Problem polega na tym, że jeśli napiszesz wiele klas, otrzymasz mnóstwo kodu standardowego (interfejs, interfejsy fabryczne i ich implementacje). Tak więc starałem się uzyskać przewagę tego projektu (testowalności) bez kosztów (dużo kodu standardowego). –

+0

To jest zdecydowanie niebezpieczeństwo przyjęcia takiego podejścia podczas pisania próbnych testów. Pracuję nad bazą kodową, w której duża część logiki została zmielona na drobny proszek przez kamienie młyńskie z refaktoryzacji próbnej. W tym przypadku, biorąc pod uwagę, że 'DataSource' już istnieje, reprezentuje dobrze zdefiniowaną, zamkniętą koncepcję i ma już implementacje, uważam, że ryzyko przerostu bojlera jest niewielkie. –

Powiązane problemy