2017-06-19 4 views
5

Ich habe einige Code asynchron konvertiert. Der ursprüngliche Komponententest verwendete die Annotation @Test(expected = MyExcpetion.class), aber ich glaube nicht, dass dies funktioniert, da die Ausnahme, auf die ich zugreifen möchte, in java.util.concurrent.ExcutionException eingeschlossen ist. Ich versuchte meine Zukunft wie diese fordern aber meine Behauptung ist nach wie vor versagt und ich liebe nicht, dass ich in return nullWie kann ich Ausnahmen in der Zukunft testen?

myApiCall.get(123).exceptionally((ex) -> { 
assertEquals(ex.getCause(),MyCustomException.class) 
return null 
} 

Ich habe auch versucht, diesen Geschmack zu verleihen hatte aber immer noch nicht funktioniert

myApiCall.get(123).exceptionally((ex) -> { 
assertThat(ex.getCause()) 
    .isInstanceOF(MyException.class) 
    .hasMessage("expected message etc") 
return null; 
} 

Meine API löst nur eine Ausnahme aus, wenn sie keine ID finden kann. Wie sollte ich das richtig testen? Kann ich diese ursprüngliche Anmerkung trotzdem verwenden?

mein API-Aufruf reicht bis zu db wenn ausgeführt. In diesem Test richte ich meine Zukunft ein, um einen Fehler zurückzugeben, so dass es nicht wirklich versucht, mit irgendetwas zu kommunizieren. der im Test befindlichen Code sieht wie folgt aus

public class myApiCall { 
    public completableFuture get(final String id){ 
    return myService.getFromDB(id) 
    .thenApply( 
     //code here looks at result and if happy path then returns it after 
     //doing some transformation 
     //otherwise it throws exception 
    ) 
    } 
} 

in der Unit-Test-I myService.getFromDB(id) zwingen falsche Daten zurück, damit ich Ausnahme testen können und halten Sie auch diese ein Unit-Test erreichen, nicht zu db usw.

+0

Vermutlich sind Ihre Delegierten zu einem anderen Thread "Get". Beachten Sie, dass JUnit nicht auf die Fertigstellung wartet. Wenn die Testmethode beendet wird, wird der Java-Prozess beendet. Stattdessen auf die CF "kommen", um auf das Ergebnis zu warten. Wenn es eine Ausnahme gibt, packen Sie sie aus und bestätigen Sie sie (oder wiederholen Sie sie und verwenden Sie Ihr 'expected'). –

+0

Was ist myApiCall hier? – fxrbfg

+0

API-Aufruf reicht nur für Daten db aus.Lassen Sie mich etwas in den Code oben so aktualisieren, vielleicht ist es klarer – Barry

Antwort

4

Nehmen wir an, Ihre API genannt wirft, wenn sie mit 0:

public static CompletableFuture<Integer> apiCall(int id) { 
    return CompletableFuture.supplyAsync(() -> { 
    if (id == 0) throw new RuntimeException("Please not 0!!"); 
    else return id; 
    }); 
} 

Sie können testen, ob es mit dem folgenden Code wie erwartet funktioniert (ich verwende TestNG, aber ich vermute, dass es nicht zu schwierig sein wird, in eine JUnit zu übersetzen Test):

Beachten Sie, dass der zweite Test die Tatsache verwendet, dass die ExecutionException-Nachricht den ursprünglichen Ausnahmetyp und die ursprüngliche Ausnahmebedingung enthält und die Erwartung mit einer Regex erfasst. Wenn dies mit JUnit nicht möglich ist, können Sie result.get() in einem try/catch-Block aufrufen und throw e.getCause(); im catch-Block aufrufen. Mit anderen Worten, so etwas wie diese:

@Test(expectedExceptions = RuntimeException.class, 
     expectedExceptionsMessageRegExp = "Please not 0!!") 
public void test_ex() throws Throwable { 
    CompletableFuture<Integer> result = apiCall(0); 
    try { 
    result.get(); 
    } catch (ExecutionException e) { 
    throw e.getCause(); 
    } 
} 
+0

Ich hatte Probleme mit dem JUnit Geschmack so ging mit dem Stil Ihres Tests über die Verpackung '.get' in Versuch fangen und geltend machen wegen der Ausnahme und das hat funktioniert. Als ich '@Test (expected = MyException.class) probierte, hatte ich Probleme, aber ich frage mich, ob ich die Ausnahme erneut auslösen könnte, aber ich wollte tiefere Behauptungen im Fehlerfall machen, also war ok mit try/catch. – Barry

0

können Sie auch versuchen, alternative Option:

import org.hamcrest.core.IsInstanceOf; 
import org.junit.rules.ExpectedException; 

public class Test() { 

    @Rule 
    public ExpectedException thrown = ExpectedException.none(); 

    @Test 
    public void myApiCallTest() { 
     thrown.expect(ExcutionException.class); 
     thrown.expectCause(IsInstanceOf.instanceOf(MyException.class)); 
     thrown.expectMessage("the message you expected"); 
     myApiCall.get(""); 
    } 
} 

dass Unter der Annahme:

public class myApiCall { 
    public completableFuture get(final String id) { 
     // ... 
     throw new ExcutionException(new MyException("the message you expected")) 
    } 
} 
1

die einfache Sache zu tun in JUnit-4. Erinnerst du dich an die @RunWith Annotation? Ja, schreiben Sie Ihre eigenen TestRunner die Ausnahme abzufangen, bevor der junit erwarteten Ausnahmeprozessor aufgerufen wird, zum Beispiel:

public class ConcurrentRunner extends BlockJUnit4ClassRunner { 

    public ConcurrentRunner(Class<?> klass) throws InitializationError { 
     super(klass); 
    } 


    @Override 
    protected Statement possiblyExpectingExceptions(FrameworkMethod method, 
                Object test, 
                Statement next) { 
     return super.possiblyExpectingExceptions(
       method, test, throwingActualException(next) 
     ); 
    } 

    private Statement throwingActualException(Statement next) { 
     return new Statement() { 
      @Override 
      public void evaluate() throws Throwable { 
       try { 
        next.evaluate(); 
       } catch (ExecutionException | CompletionException source) { 
        throw theActualExceptionOf(source); 
       } 
      } 

      private Throwable theActualExceptionOf(Exception source) { 
       return source.getCause() != null ? source.getCause() : source; 
      } 
     }; 
    } 
} 

nur mit @RunWith(ConcurrentRunner.class) auf dem Test kommentiert, müssen Sie nicht Ihren Test-Code überhaupt ändern. zum Beispiel:

@RunWith(ConcurrentRunner.class) 
public class ConcurrentExpectedExceptionTest { 

    @Test(expected = IllegalArgumentException.class) 
    public void caughtTheActualException() throws Throwable { 
     myApiCall().join(); 
    } 

    private CompletableFuture<Object> myApiCall() { 
     return CompletableFuture.supplyAsync(() -> { 
      throw new IllegalArgumentException(); 
     }); 
    } 
} 
Verwandte Themen