2009-05-18 13 views
21

私はResultSetをモックしたいです。真剣に。 私は、ResultSetからデータを解析している複雑で複雑なコードを1つリファクタリングしています。コードが同じように動作するようにします。ですから、これをテストできるようにリファクタリングされている作品の単体テストを書く必要があります。モックシーケンスをlooooong書き、ResultSetをデータでいっぱいにする簡単な方法

  1. 使用EasyMock:

    グーグル後、私は2つのアイデアを思い付きました。非常に悪い解決策:初期データを追加するのは難しい、データを変更するのは難しい、大きなテストデバッグプロミス。

  2. Apache DerbyまたはHSQLDBを使用してメモリ内DBを作成し、ファイルまたは文字列配列から埋め込み、いくつかの魔法のInMemoryDBUtils.query(sql)でクエリします。次に、そのResultSetを使用します。残念ながら、私は魔法のInMemoryDBUtilsがテストを速く書くことはできませんでした:-)。 IBMの記事「Derbyによる永続性の単体テスト・テスト」は、私が必要とするものについてはうまくいっています...

2番目のアプローチは多少容易になり、よりサポートしやすくなります。

このようなモックを作成するにはどうすればよいですか? (医者にもかかわらず、もちろん:-)?私は逃した 眉毛 いくつかの弾丸?おそらく、DBUnitはこれのためのツールですか?

答えて

11

DBUnitは、私の知る限り、結果セットを提示しませんが、メモリ・データベースを作成するのに役立ちます。

私は、この時点では、模擬フレームワークが間違ったアプローチであると言います。 Mockingは、単にデータを返すだけでなく、行動やインタラクションをテストすることで、あなたのやり方になる可能性があります。

代わりに結果セットインターフェイスを実装するか、結果セット全体を実装することなく気になるメソッドを実装するクラスへの結果セットインターフェイスの動的プロキシを作成します。テスト中のデータセットが一貫していればメモリ内のデータベースを維持するのと同じくらい簡単にクラスを維持することができますし、おそらくデバッグは簡単です。

dbunitで結果セットのスナップショットをとり、dbunitでテスト中にxmlから読み込み、ダミーの結果セットでdbunitのクラスからデータを読み込ませることで、そのクラスをバックアップできます。データが軽度に複雑な場合は、これは合理的なアプローチになります。

クラスが結合されていて、同じテストの一部として変更されたデータを読み込む必要がある場合、私はメモリデータベースを探します。それでも、私は、あなたがその依存関係を引き離すまで、実際のデータベースのコピーを使うことを検討します。

簡単なプロキシの生成方法:http://mockrunner.sourceforge.net/

private static class SimpleInvocationHandler implements InvocationHandler { 
    private Object invokee; 

    public SimpleInvocationHandler(Object invokee) { 
     this.invokee = invokee; 
    } 

    public Object invoke(Object proxy, Method method, Object[] args) 
      throws Throwable { 
     method = invokee.getClass().getMethod(method.getName(), method.getParameterTypes()); 
     if (!method.isAccessible()) { 
      method.setAccessible(true); 
     } 
     try { 
      return method.invoke(invokee, args); 
     } catch (InvocationTargetException e) { 
      throw e.getTargetException(); 
     } 
    } 
} 

public static <T> T generateProxy(Object realObject, Class... interfaces) { 
    return (T) Proxy.newProxyInstance(realObject.getClass().getClassLoader(), interfaces, new SimpleInvocationHandler(realObject)); 
} 
+0

こんにちはYishai、フィードバックありがとうございます。私は10以上のテストのそれぞれについて約100のレコードを嘲笑し、レコードは10フィールドで構成されます。これは、AFAIUは、ファイルとカスタムResultSetの実装でデータを格納するDBUnitで私を残します。返信をしてくれてありがとう。 – DiaWorD

+0

あなたは大歓迎です。私は、10のテストのそれぞれが異なるデータを必要とするならば、これは合理的なアプローチであると言います。 DBUnitを使用すると、ResultSetをXMLに書き込むことができるため、テストで参照するだけです。 – Yishai

2

可能であれば、実際のデータソースから取得した結果セットを取得し、シリアル化してファイルを保存することができます。その後、各ユニットテストの結果セットをデシリアライズすることができます。

+0

こんにちはGWLlosa、ありがとうございました。 DBUnitはデータをファイルに保存するのに役立つと思います。 – DiaWorD

1

ほとんどのResultSetメソッドを呼び出すのでなければ、区切られたテキストファイルを2次元配列にロードして、実際に必要なメソッドを実装し、残りの部分はUnsupportedOperationException(私のIDEでスタブアウトされたメソッドのデフォルト実装です)。

36

私はここからMockResultSetクラスで成功を収めてきました。 ResultSetインターフェイスを実装するクラスを作成し、各列と各行の値を設定することができます。

メソッドが適切なサイズのResultSetを使用している場合は、必要な値を簡単に返すテストを作成することができます。

MockResultSet rs = new MockResultSet("myMock"); 

rs.addColumn("columnA", new Integer[]{1}); 
rs.addColumn("columnB", new String[]{"Column B Value"}); 
rs.addColumn("columnC", new Double[]{2}); 

// make sure to move the cursor to the first row 
try 
{ 
    rs.next(); 
} 
catch (SQLException sqle) 
{ 
    fail("unable to move resultSet"); 
} 

// process the result set 
MyObject obj = processor.processResultSet(rs); 

// run your tests using the ResultSet like you normally would 
assertEquals(1, obj.getColumnAValue()); 
assertEquals("Column B Value", obj.getColumnBValue()); 
assertEquals(2.0d, obj.getColumnCValue()); 
+0

こんにちはmjd79、 お返事ありがとうございます。私の場合の問題は、テストケースごとに約100件のレコードがあり、テストケースが約10件あることです。:-)各レコードには、日付、数値、文字列を含む10個のフィールドがあります。そのため、モックデータをファイルに保存することをお勧めします。これは、ほとんどすべてのDBクライアントから直接取り込むことができるINSERT INTO形式にすることをお勧めします。 MockRunnerはかなりいいですが、私はそれ以外の場合に備えます。 お返事ありがとうございます。 – DiaWorD

+0

mockrunnerはバッチジョブテストのための簡単な結果セットを模擬する素晴らしいリソースでした。本当にありがとう! – EdgeCaseBerg

+0

これは受け入れられる回答である必要があります。 – JonyD

5

MockrunnerはCSVまたはXMLファイルを読み込むと、自動的にMockResultSetを作成することができます。ここでは

は簡単な例です。また、ConnectionとStatementをモックすることもできます。そのため、クラスパスにJDBCドライバを追加することなく、すべてのJDBCを簡単に処理できます。

5

私はこの同じケースのために何かを書いています。 Mockitoを使用して結果セットをモックすることができます。同様にresultset.next()をこのコードで嘲笑して、結果セットの模擬行をループすることもできます。

// two dimensional array mocking the rows of database. 
String[][] result = { { "column1", "column2" }, { "column1", "column2" } }; 

@InjectMocks 
@Spy 
private TestableClass testableClass; 

@Mock 
private Connection connection; 

@Mock 
private Statement statement; 

@Mock 
private ResultSet resultSet; 

@BeforeTest 
public void beforeTest() { 
    MockitoAnnotations.initMocks(this); 
} 

@BeforeMethod 
public void beforeMethod() throws SQLException { 
    doAnswer(new Answer<Connection>() { 
     public Connection answer(InvocationOnMock invocation) 
       throws Throwable { 
      return connection; 

     } 
    }).when(testableClass).getConnection(); 

    when(connection.createStatement()).thenReturn(statement); 
    when(statement.executeQuery(anyString())).thenReturn(resultSet); 
    final AtomicInteger idx = new AtomicInteger(0); 
    final MockRow row = new MockRow(); 

    doAnswer(new Answer<Boolean>() { 

     @Override 
     public Boolean answer(InvocationOnMock invocation) throws Throwable { 
      int index = idx.getAndIncrement(); 
      if (result.length > index) { 
       String[] current = result[index]; 
       row.setCurrentRowData(current); 
       return true; 
      } else 
       return false; 

     } 

     ; 
    }).when(resultSet).next(); 

    doAnswer(new Answer<String>() { 

     @Override 
     public String answer(InvocationOnMock invocation) throws Throwable { 
      Object[] args = invocation.getArguments(); 
      int idx = ((Integer) args[0]).intValue(); 
      return row.getColumn(idx); 
     } 

     ; 
    }).when(resultSet).getString(anyInt()); 
} 

static class MockRow { 
    String[] rowData; 

    public void setCurrentRowData(String[] rowData) { 
     this.rowData = rowData; 
    } 

    public String getColumn(int idx) { 
     return rowData[idx - 1]; 
    } 
} 
関連する問題