2009-07-17 10 views
1

私はmockitoで単純なDAOレイヤーをテストしていますが、基本的にはインターフェイスをテストするのが難しい問題を発見しました。私はあなたに私にいくつかの洞察力を与えることができるかどうか疑問に思っていました...テスト容易化のためのリファクタリングの必要性

これは私がテストしたい方法です:

public Person getById(UserId id) { 
    final Person person = new PersonImpl(); 

    gateway.executeQuery(GET_SQL + id.getUserId(), new ResultSetCommand(){ 
     public int work(ResultSet rs) throws SQLException { 
     if(rs.next()){ 
      person.getName().setGivenName(rs.getString("name")); 
      person.getName().setFamilyName(rs.getString("last_name")); 
     } 
     return 0; 
     } 
    }); 
    return person; 
    } 

私はDatabaseGatewayを使用するJavaコードとSQ​​Lの間、私のインタフェースであり、その方法は、匿名クラスを受け入れ、これはゲートウェイのメソッドのexecuteQueryです:

public int executeQuery(String sql, ResultSetCommand cmd) { 
    try{ 
     Connection cn = createConnection(); 
     PreparedStatement st = cn.prepareStatement(sql); 
     int result = cmd.work(st.executeQuery()); 
     cn.close(); 
     return result; 
    }catch(Exception e){ 
     throw new RuntimeException("Cannot Create Statement for sql " + sql,e); 
    } 
    } 

事があるため、その匿名クラスで、PersonDAOをテストするために難しくなってきた、ということです。

私は誰かがよりよい設計を(私はシンプルな1があると確信しているが、私はちょうどそれを見つけるように見えることはできません)をお勧めている場合でも、匿名クラスを削除し、全体のコードをリファクタリングすることができます。提案のため

みんなありがとう。

PD:テスト

public void testGetPersonById(){ 
    DatabaseGateway gateway = mock(DatabaseGateway.class); 
    when(gateway.executeQuery(anyString(),any(ResultSetCommand.class))); 
    PersonDAO person_dao = new PersonDAOImpl(gateway); 

    Person p = person_dao.getById(new UserId(Type.viewer,"100")); 
    } 

参照を行うのは難しいです:あなたは、さらに情報が必要な場合、


EDIT気軽に? ResultCommandはモックの一部です。私はそのコードをテストすることにも興味があります。その特定のコマンドについて別のテストを行うべきですか?

+0

難しくなっているテストの例を挙げてください。 –

+0

Javaはうまくいきませんが、ResultSetCommandは関係ありません。 GetPersonByIdをテストして、有効なUserIdが与えられたときに正しいPersonを返し、無効なPersonIdが与えられたときに例外をスローします。正しく動作する場合は、特定のResultSetCommandを使用しても構いません。 –

答えて

1

は、代わりに匿名クラスを使用するには、別途インターフェースとその実装を作成することができます。メソッドexecuteQueryはStringとこのインタフェースをパラメータとして持ちます。

だからあなたのテストは同じままであろう。別のテスト(インターフェイス実装のテスト)で作業メソッドを分けることができます。これはテストするのが難しいようです。あなたは空想取得し、モックResultSetでそれにコールバックをシミュレートし、その後、ResultSetCommand引数を "取り込む" ことができ

public int executeQuery(String sql, MyInterface cmd) { 
    try{ 
     Connection cn = createConnection(); 
     PreparedStatement st = cn.prepareStatement(sql); 
     int result = cmd.work(st.executeQuery()); 
     cn.close(); 
     return result; 
    }catch(Exception e){ 
     throw new RuntimeException("Cannot Create Statement for sql " + sql,e); 
    } 
    } 
+0

PersonDAO用のテストと匿名のResultSetCommandクラス用テストの2つのテストを行います。 –

+0

@Pabloはい、テストは独立しています(ユニットテストを行い、統合テストはしないと考えていることを考慮して)。「より大きな画像テスト」が必要な場合は、executeQueryをResultSetだけを返し、PersonDAOで使用することができます。 –

+0

私は最初にそのアプローチ(RSを返す)を試みましたが、それは奇妙なエラーを投げました、私は信じていますRSは、接続が閉じられたかそれと何かのために働くことができなかったので、呼び出し側はRSまたは接続のいずれかを閉じる責任を負います。 –

0

public Person getById(UserId id) { 
    final Person person = new PersonImpl(); 

    gateway.executeQuery(GET_SQL + id.getUserId(), new MyInterfaceImpl(person)); 
    return person; 
} 

結果は次のようなものになるだろう

/** 
* Custom matcher - always returns true, but captures the 
* ResultSetCommand param 
*/ 
class CaptureArg extends ArgumentMatcher<ResultSetCommand> { 
    ResultSetCommand resultSetCommand; 
    public boolean matches(Object resultSetCommand) { 
     resultSetCommand = resultSetCommand; 
     return true; 
    } 
} 

public void testGetPersonById(){ 
    // setup expectations... 
    final String lastName = "Smith"; 
    final String firstName = "John"; 
    final CaptureArg captureArg = new CaptureArg(); 
    DatabaseGateway gateway = mock(DatabaseGateway.class); 
    ResultSet mockResultSet = mock(ResultSet.class); 
    when(gateway.executeQuery(anyString(), argThat(captureArg))); 
    when(mockResultSet.next()).thenReturn(Boolean.True); 
    when(mockResultSet.getString("name")).thenReturn(firstName); 
    when(mockResultSet.getString("last_name")).thenReturn(lastName); 

    // run the test... 
    PersonDAO person_dao = new PersonDAOImpl(gateway); 
    Person p = person_dao.getById(new UserId(Type.viewer,"100")); 

    // simulate the callback... 
    captureArg.resultSetCommand.work(mockResultSet); 

    // verify 
    assertEquals(firstName, person.getName().getGivenName()); 
    assertEquals(lastName, person.getName().getFamilyName()); 
} 

私はこのように葛藤しています - 私はあなたがテストしているメソッドの多くの内部を公開しています。しかし、少なくともそれはオプションです。

関連する問題