0

내 데이터베이스 코드를 단위 테스트하는 데 Mockito, DBUnit 및 을 사용하고 있습니다. 물론 통합 테스트도 쓰고 있습니다.정적 메서드를 통해 액세스 할 수 있음에도 불구하고 DataSource Dependency Injection을 모의 사용하는 방법

테스트중인 시스템 (테스트중인 클래스)에 조롱 된 DataSource을 삽입하는 방법을 이해하는 데 문제가 있습니다. DataSource은 연결 풀링에 사용되므로 다른 클래스는이 클래스 DataSource의 인스턴스를 검색하기 위해 같은 클래스에서 static 메서드를 호출 할 수 있습니다. 즉, DataSource은 어디서나 생성자에 주입되지 않으므로 내 테스트에는 조롱 된 DataSource을 삽입 할 생성자가 없습니다.

내 실제 코드의 논리를 변경하여 개인 변수가 null인지 확인하고, 그렇다면 주입 된 데이터 소스 (테스트에만 필요하기 때문에 잘못된 디자인)를 사용합니다. 정적 메서드는 연결 풀의 소스를 검색 (더 나은 디자인).

DataSource을 생성자가 받아 들일 수있는 클래스가 아닌 클래스에 삽입하려면 대신 정적 메서드를 호출하여 종속성을 검색하면되므로 어떻게해야합니까?

클래스

public DBConnection(DBSource dbSource) { // <--- Constructor only required for test purposes :(
     this.dbSource = dbSource; 
    } 

    public final void createCompsDB() { 
     Connection conn = null; 
     Statement statement = null; 
     try { 
      if(dbSource==null){ 
       conn = DBSource.getInstance().getConnection(); 
      }else{ 
       conn = dbSource.getConnection(); /** Likely bad design, since dbSource is only NOT null for tests, so that you can inject the mocked datasource :( */ 
      } 
      statement = conn.createStatement(); 
      statement.executeUpdate("CREATE DATABASE placesdb"); 
      System.out.println("Database created..."); 
     } catch (SQLException e) { 
       // ... 
      } 
     } finally { 
      // Close Resources... 
     } 
    } 
} 

테스트 클래스를 테스트하는 - 테스트 DBSource.java

protected DBSource() throws IOException, SQLException, PropertyVetoException { 
     ds = new BasicDataSource(); 
     ds.setDriverClassName("org.postgresql.Driver"); 
     ds.setUsername("user"); 
     ds.setPassword("pass"); 
     ds.setUrl("jdbc:postgresql://localhost:5432/placesdb"); 
    } 

    public static DBSource getInstance() { // <--- Static method means dependent classes don't need to accept injections 
     if (datasource == null) { 
      datasource = new DBSource(); 
      return datasource; 
     } else { 
      return datasource; 
     } 
    } 

    public Connection getConnection() throws SQLException { 
     return this.ds.getConnection(); 
    } 
} 

답변

1

M

public class DBConnectionTest { 
     final Statement statement = mock(Statement.class); 
     final Connection connection = mock(Connection.class); 
     final DBSource dataSource = mock(DBSource.class); 

    @Before 
    public void setUp() throws SQLException, IOException, PropertyVetoException { 
     when(dataSource.getConnection()).thenReturn(connection); 
     when(connection.createStatement()).thenReturn(statement); 
    } 

    @Test 
    public void testCreateCompDBIfNotAlready() throws Exception { 
     DBConnection dbConnection = new DBConnection(localDB, dataSource); /** This constructor is only needed for testing :(. How do I avoid it since all the classes I need to test don't require the dependency to be injected? */ 
     dbConnection.createCompsDB();  
     verify(statement).executeUpdate("CREATE DATABASE PLACES"); 
    } 
} 

전달 PowerMockito를 사용하여 정적 클래스 메서드를 수행 할 수 있습니다. 테스트 클래스는 같은 것을해야한다 :

@RunWith(PowerMockRunner.class) 
@PrepareForTest(DBSource.class) 
public class DBConnectionTest { 
    @Mock 
    final Statement statement; 
    @Mock 
    final Connection connection; 
    @Mock 
    final DBSource dbsource; 

    @Before 
    public void setUp() throws SQLException, IOException, PropertyVetoException { 
     PowerMockito.mockStatic(DBSource.class); 
     when(DbSource.getInstance()).thenReturn(dbsource); 
     when(dbsource.getConnection()).thenReturn(connection); 
     when(connection.createStatement()).thenReturn(statement); 
    } 

    @Test 
    public void testCreateCompDBIfNotAlready() throws Exception { 
     DBConnection dbConnection = new DBConnection(localDB); // No test-only constructor anymore 
     dbConnection.createCompsDB();  
     verify(statement).executeUpdate("CREATE DATABASE PLACES"); 
    } 
} 

당신은 PowerMock과 조롱에 대한 here 자세한 내용을보실 수 있습니다.

+0

고마워요. @asch, 정적이 아니기 때문에 왜'DBSource.class'를 모의했는지 혼란 스럽지만'.getInstance()'method *는 정적입니다. 만약 당신이 제안한'when (DataSource.getConnection()). thenReturn (connection);을 사용한다면'DataSource'를 참조하는 에러를 리턴합니다. 에러를 반환하는'DBSource'를 의미한다고 가정합니다. 내'setUp()'메소드의 처음 2 줄을 다음과 같이 바 꾸었습니다 :'PowerMockito.mockStatic (DBSource.class); (DBSource.getInstance(). getConnection()). thenReturn (connection);'이 경우 NullPointerException이 발생하며 'DBSource'가 null (?)이라고 표시됩니다. 내가 도대체 ​​뭘 잘못하고있는 겁니까? – Nova

+0

기본적으로 정적 인 것이 아님에도 불구하고'DBSource.class'를 조롱하고 있기 때문에'when (DBSource.getInstance(). getConnection()). then return (conn ection); '으로 NullPointerException을 얻고 있습니다. 어쨌든'.getInstance()'메소드가 있습니다. 당신의 생각? 감사합니다 – Nova

+0

그래서 getInstance도 조롱해야합니다. 솔루션을 업데이트했습니다. – asch