2012-10-22 15 views
5

Ho un'applicazione Java che utilizza molto java.sql.Connection in un database.Errore DB in modo deterministico per il test

Voglio verificare che, se il database non è disponibile, i miei servizi restituiscono i codici di errore appropriati (distinguendo tra problemi temporanei e permanenti, ad esempio HTTP 500 e 503).

Per il test, la mia applicazione si collega a un database h2 incorporato, in memoria, locale; l'applicazione non ne è a conoscenza, solo il mio test di integrazione è.

Come posso fare in modo che le scritture sul database falliscano in modo deterministico, ad es. agganciarsi ai commit e farli lanciare un'abitudine SQLException? Voglio un booleano globale "database non disponibile" nel codice di test che interessa tutte le connessioni e fa sì che la mia applicazione eserciti la sua logica di riconnessione.

(avevo iniziato con il proxy Connection e mettendo un if(failFlag) throw new MySimulateFailureException() in commit(), ma questo non ha interferito PreparedStatement.executeUpdate(); prima di imbarcarsi sul proxying il PreparedStatement troppo - è un sacco di metodi - mi piacerebbe essere insegnato! un modo migliore ...)

+2

questo può sembrare stupido, ma perché non interrompere il servizio di database? o bloccare la porta su un firewall per negare il traffico per un po '? – Serdalis

+2

Di solito vado sul percorso del proxy che descrivi sopra. Se qualcuno ha un approccio migliore sarei interessato a sentirlo. – Keppil

risposta

0

ho finito per fare il mio riflesso involucro Java che intercetta Connection.commit ei PreparedStatement.execute... metodi.

Il mio codice finale nella mia classe 'DBFactory':

@SuppressWarnings("serial") 
public class MockFailureException extends SQLException { 
    private MockFailureException() { 
     super("The database has been deliberately faulted as part of a test-case"); 
    } 
} 

private class MockFailureWrapper implements InvocationHandler { 

    final Object obj; 

    private MockFailureWrapper(Object obj) { 
     this.obj = obj; 
    } 

    @Override public Object invoke(Object proxy, Method m, Object[] args) throws Throwable { 
     if(dbFailure && ("commit".equals(m.getName()) || m.getName().startsWith("execute"))) 
      throw new MockFailureException(); 
     Object result; 
     try { 
      result = m.invoke(obj, args); 
      if(result instanceof PreparedStatement) 
       result = java.lang.reflect.Proxy.newProxyInstance(
         result.getClass().getClassLoader(), 
         result.getClass().getInterfaces(), 
         new MockFailureWrapper(result)); 
     } catch (InvocationTargetException e) { 
      throw e.getTargetException(); 
     } catch (Exception e) { 
      throw new RuntimeException("unexpected invocation exception: " + e.getMessage()); 
     } 
     return result; 
    } 

} 


public Connection newConnection() throws SQLException { 
    Connection connection = DriverManager.getConnection("jdbc:h2:mem:"+uuid+";CREATE=TRUE;DB_CLOSE_ON_EXIT=FALSE"); 
    return (Connection)java.lang.reflect.Proxy.newProxyInstance(
      connection.getClass().getClassLoader(), 
      connection.getClass().getInterfaces(), 
      new MockFailureWrapper(connection)); 
} 
1

Penso che questo sia un buon candidato per l'utilizzo di aspects. Con es. Spring è facilissimo aggiungere interi pacchetti o solo determinati metodi che si desidera non riuscire - in particolare è possibile avere un consiglio before sempre lanciando un ConnectException o fare qualcosa di più avanzato con il consiglio around.

Cheers,

Problemi correlati