2012-10-22 9 views
5

Mam aplikacji Java, która używa wiele java.sql.Connection do bazy danych.Niech deterministically DB do testowania

Chcę przetestować, że jeśli baza danych jest niedostępna, moje usługi zwracają odpowiednie kody błędów (rozróżniając tymczasowe i trwałe problemy, np. HTTP 500 i 503).

Do testowania moja aplikacja łączy się z osadzoną lokalną bazą danych h2 w pamięci; aplikacja nie jest tego świadoma, tylko mój test integracyjny jest.

W jaki sposób można dokonać deterministycznego zapisu w bazie danych, np. haczyk w commitach i zmuszanie ich do rzucania niestandardowego SQLException? Chcę globalnej "bazy danych jest niedostępna" boolean w kodzie testowym, który wpływa na wszystkie połączenia i sprawia, że ​​moja aplikacja wykonuje swoją logikę ponownego połączenia.

(zacząłem od buforowania Connection i kładąc if(failFlag) throw new MySimulateFailureException() w commit(), ale to nie złapać PreparedStatement.executeUpdate(); zanim rozpocząć buforowania na PreparedStatement też - jego wiele sposobów - Chciałabym się nauczyć! lepszy sposób ...)

+2

to może brzmieć głupio, ale dlaczego nie zatrzymać usługę bazy danych? lub zablokować port na zaporze sieciowej, aby nieco odmówić ruchu? – Serdalis

+2

Zwykle przechodzę na trasę proxy, którą konspektujesz powyżej. Jeśli ktokolwiek ma lepsze podejście, byłbym zainteresowany jego wysłuchaniem. – Keppil

Odpowiedz

0

skończyło się moje własne odbicie Java opakowanie, które przechwytuje Connection.commit i metod PreparedStatement.execute....

Mój ostatni kod w mojej klasie „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

Myślę, że jest to dobry kandydat do używania aspects. Z np. Spring jest bardzo łatwo wskazać całe pakiety lub tylko niektóre metody, które mają zawieść - konkretnie możesz mieć porady before zawsze rzucając ConnectException lub zrobić coś bardziej zaawansowanego z poradą around.

Cheers,