2016-04-04 2 views
0

ich mit einer Oracle 10g-Datenbank zu tun habe und die folgende gespeicherte Prozedur vorgesehen:Mock Oracle CallableStatement.getCursor()

procedure get_synopsis (
    p_id in my_schema.products.p_id%type, 
    p_synopses out sys_refcursor); -- cursor of - synopsis_type, synopsis_text 

In meinem Java-Code bereite ich die Anweisung auf diese Weise:

String idForDb = fromIdUrlToIdDb(prodIdUrl); 
statement.registerOutParameter(1, OracleTypes.VARCHAR); 
statement.setString(1, idForDb); 
statement.registerOutParameter(2, OracleTypes.CURSOR); 

Und ich bekomme die Daten, die ich auf diese Weise brauchen:

String defaultSyn, nonDefSyn; 

String returnedId = ((OracleCallableStatement)stm).getString(1); 
try (ResultSet synopses = ((OracleCallableStatement)stm).getCursor(2)){ // p_synopses - cursor of: synopsis_type, synopsis_text 

    while(synopses!=null && synopses.next()){ 

     String type = synopses.getString(1) != null ? synopses.getString(1).toUpperCase() : null; 

     if(type != null){ 

      StringBuilder sb = new StringBuilder(); 
      BufferedReader br = new BufferedReader(synopses.getClob(2).getCharacterStream()); 
      String line; 

      while ((line = br.readLine()) != null) { 
       sb.append(line).append("\n"); 
      } 

      if("DEFAULT".equals(type)){ 
       defaultSyn = sb.toString(); 
      }else if("NONDEFAULT".equals(type)){ 
       nonDefSyn = sb.toString(); 
      } 

      // ... 
     } 
    } 
} 

in meinem Test s Wie kann ich spotten (OracleCallableStatement) stm.getCursor (2)?

Ich bin mit org.jmock.Mockery versucht, aber ohne Erfolg:

Mockery mockery_inner = new Mockery(); 
final ResultSet mocked_resultset = mockery_inner.mock(ResultSet.class); 
mockery_inner.checking(new Expectations() {{ 
    allowing(mocked_resultset).getString(1); will(returnValue("TEST_SYNOPSES-TYPE")); 
    allowing(mocked_resultset).getClob(2); will(returnValue("TEST_CLOooooooB")); 
}}); 

Mockery mockery = new Mockery(); 
final CallableStatement statement = mockery.mock(CallableStatement.class); 

mockery.checking(new Expectations() {{ 
    allowing(statement).getString(1); will(returnValue("TEST_RETURNED-PROD-ID")); 
    allowing(statement).getCursor(2); will(returnValue(mocked_resultset)); // cannot find symbol getCursor(int). Location: interface java.sql.CallableStatement 
}}); 

Grund klar ist: kann nicht Symbol GetCursor (int) finden. Ort: Schnittstelle java.sql.CallableStatement.

Wenn ich versuche, erlaubt ((OracleCallableStatement) statement) .getCursor (2) ich "java.lang.ClassCastException. Com.sun.proxy $ Proxy6 kann nicht auf oracle.jdbc.driver.OracleCallableStatement gegossen werden" . Hinweis: OracleCallableStatement ist keine Schnittstelle und kann daher nicht mit Mockerware verspottet werden.

Ich versuche, eine "manuelle" mock zu verwenden, aber ich habe Probleme, eine Instanz zu schaffen ..

class MockOracleCallableStatement implements OracleCallableStatement { 

    ResultSet mocked_resultset; 

    public MockOracleCallableStatement(){ 

     Mockery mockery_inner = new Mockery(); 
     mocked_resultset = mockery_inner.mock(ResultSet.class); 
     mockery_inner.checking(new Expectations() {{ 
      allowing(mocked_resultset).getString(1); will(returnValue("DEFAULT")); // will pick value from an array 
      allowing(mocked_resultset).getClob(2); will(returnValue("TEST_CLOooooooooooB")); 
     }}); 
    } 

    @Override 
    ResultSet getCursor(int paramIndex) throws SQLException{ 
     return mocked_resultset; 
    } 
    @Override 
    String getString(int paramIndex) throws SQLException{ 
     return "mockedGetString1--test"; 
    } 
} 

Antwort

1

Auf den Punkt gebracht, DO NOT .

Mocking JDBC (zusammen mit vielen anderen Dingen) ist ein Idiot Auftrag und wird nicht die Dinge testen, die Sie denken, es testet, aber kostet Sie eine riesige Menge an Zeit.

Sie sollten wirklich einen Integrationstest schreiben, der tatsächlich in Ihre Datenbank geht. Nur so können Sie überprüfen, ob Ihr Datenbankcode korrekt ist. Wenn Sie können, verwenden Sie genau die gleiche Version der Datenbank wie in der Produktion, wenn Sie keine In-Memory-Datenbank wie H2 * verwenden.

Ich schrieb eine article for JAX magazine zu diesem genauen Thema, die viel detaillierter gehen wird.

  • Obwohl dies andere Probleme hat aufgrund von Kompatibilitäts
+0

Wir haben bereits Integrationstests an seinem Platz.Ich brauche diesen Komponententest nur, um zu testen, ob die Antwort korrekt in den erwarteten JSON serialisiert ist. Könnte wahrscheinlich mit etwas Refactoring diese Funktionalitäten dann trennen. Danke –

+1

@Ga Sacchi Wenn Sie eine 100% -Testabdeckung anstreben, könnte es schwierig sein, dieses Ziel zu erreichen. Allerdings stimmte ich zu, dass Ihr spezielles Problem wahrscheinlich durch zu viel Logik in der Datenzugriffsebene verursacht wird. Sie könnten eine separate Klasse erstellen und nur das Ergebnisobjekt "Synopsen" vortäuschen. – RZet

+1

Lassen Sie in diesem Fall Ihre Datenzugriffsebene ein Objekt zurückgeben, das Sie besitzen und kontrollieren, und führen Sie Ihre Integrationstests dagegen aus. Sie können dann separat testen, ob die Json-Konvertierung von diesem Objekt korrekt ausgeführt wird. – tddmonkey

0

Es scheint, wie ich Mockito ..

OracleCallableStatement oraCSMock = Mockito.mock(OracleCallableStatement.class); 

verwenden können UPDATE: Die Methode CLOB.getDBAccess (Verbindung) ist eine statische Methode der Klasse CLOB und kann daher nicht mit Mockito verspottet werden. (Sie können Statiken mit Powermock verspotten).

landete ich dann Tests nur die 404 Fall up:

ResultSet mocked_resultset = Mockito.mock(ResultSet.class); 

doReturn(null) // cant' use "DEFAULT" since getClob() will throw npe anyway. Will test just 404 then. 
.when(mocked_resultset).getString(1); 

doReturn(false) 
.when(mocked_resultset).next(); // or just return null in getCursor(2) 


OracleCallableStatement statement = Mockito.mock(OracleCallableStatement.class); 

doReturn("testID") 
.when(statement).getString(1); 

doReturn(mocked_resultset) 
.when(statement).getCursor(2);