2013-10-19 29 views
6

Tôi có một lớp, kết nối với cơ sở dữ liệu H2 và chạy một số câu lệnh SQL.Làm thế nào để giả lập 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(); 
     } 
    } 
... 
} 

Tôi muốn viết một bài kiểm tra đơn vị, trong đó xác nhận, rằng trong phương pháp open một câu lệnh SQL nào đó (DROP TABLE IF EXISTS PERSON) được thực thi.

Để làm được điều này, tôi đã viết như sau kiểm tra:

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

Nhưng nó không hoạt động - thay vì kết nối giả, DriverManager lợi nhuận kết nối thực.

Làm cách nào để khắc phục sự cố (làm cho DriverManager trả về kết nối mô phỏng trong thử nghiệm)?

Đây là pom.xml của dự án của tôi, có thể có sự cố ở đó.

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

Trả lời

6

Công cụ này hoạt động (chú ý đến hàng nhập khẩu):

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

Cách thông thường để làm điều này sẽ là yếu tố tạo ra kết nối vào lớp khác và tiêm một thể hiện của lớp đó vào lớp được đề cập. Sau đó bạn có thể thử lớp mới đó.

Trong trường hợp của bạn, một cái gì đó như thế này:

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

} 

Trong trường hợp đặc biệt này, thậm chí còn tốt hơn có lẽ sẽ sử dụng giao diện JDBC chuẩn DataSource thay vì riêng lớp nhà máy kết nối của bạn:

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

Tôi đã thực hiện việc này e (xem http://altruix.wordpress.com/portfolio/project-control-center/ để biết chi tiết). Vấn đề là nếu bạn viết rất nhiều các lớp bạn kết thúc với rất nhiều mã boilerplate (giao diện, giao diện nhà máy và triển khai của chúng). Vì vậy, tôi đã cố gắng để có được lợi thế của thiết kế đó (testability) mà không có chi phí (rất nhiều mã boilerplate). –

+0

Đó chắc chắn là mối nguy hiểm khi thực hiện phương pháp này khi viết các thử nghiệm dựa trên giả. Tôi làm việc trên một codebase, nơi mà phần lớn logic đã được nghiền thành bột mịn bởi các millstones tái cấu trúc mô phỏng. Trong trường hợp này, cho rằng 'DataSource' đã tồn tại, đại diện cho một khái niệm khép kín, được định nghĩa rõ ràng, và đã triển khai, tôi sẽ nghĩ rằng nguy cơ phát triển quá mức của boilerplate là nhỏ. –

Các vấn đề liên quan