2009-05-15 7 views
30

मुझे पता है कि एक कर JUnit में एक 'उम्मीद' अपवाद परिभाषित कर सकते हैं,:जुनीट: एक लपेटा हुआ अपवाद 'उम्मीद' करने के लिए संभव है?

@Test(expect=MyException.class) 
public void someMethod() { ... } 

लेकिन क्या होगा अगर वहाँ हमेशा एक ही अपवाद है, लेकिन विभिन्न 'नेस्ट' कारणों साथ।

कोई सुझाव?

+1

महत्वहीन अतिरिक्त नोट: है यह "उम्मीद = ...", नहीं "उम्मीद = ..." – hoijui

+2

शामिल करने के लिए मैं विश्वास नहीं कर सकता JUnit 5 जाहिरा तौर पर अपनी टिप्पणी के वाक्य रचना संवर्धित नहीं किया गया है इस। –

उत्तर

21

आप परीक्षण कोड को एक कोशिश/पकड़ ब्लॉक में लपेट सकते हैं, फेंक दिया अपवाद पकड़ सकते हैं, आंतरिक कारण, लॉग/जोर/जो भी हो, और फिर अपवाद (अगर वांछित) को फिर से हटा दें।

5

तुम हमेशा इसे मैन्युअल रूप से कर सकता है:

@Test 
public void someMethod() { 
    try{ 
     ... all your code 
    } catch (Exception e){ 
     // check your nested clauses 
     if(e.getCause() instanceof FooException){ 
      // pass 
     } else { 
      Assert.fail("unexpected exception"); 
     } 
    } 
4

मैं उस उद्देश्य के लिए एक छोटे से JUnit विस्तार से लिखा था।

import static org.junit.Assert.assertTrue; 
import static org.junit.Assert.fail; 

import java.util.Arrays; 

public class AssertExt { 
    public static interface Runnable { 
     void run() throws Exception; 
    } 

    public static void assertExpectedExceptionCause(Runnable runnable, @SuppressWarnings("unchecked") Class[] expectedExceptions) { 
     boolean thrown = false; 
     try { 
      runnable.run(); 
     } catch(Throwable throwable) { 
      final Throwable cause = throwable.getCause(); 
      if(null != cause) { 
       assertTrue(Arrays.asList(expectedExceptions).contains(cause.getClass())); 
       thrown = true; 
      } 
     } 
     if(!thrown) { 
      fail("Expected exception not thrown or thrown exception had no cause!"); 
     } 
    } 
} 

अब आप इतनी तरह की उम्मीद नेस्टेड अपवाद के लिए जाँच कर सकते हैं:

import static AssertExt.assertExpectedExceptionCause; 

import org.junit.Test; 

public class TestExample { 
    @Test 
    public void testExpectedExceptionCauses() { 
     assertExpectedExceptionCause(new AssertExt.Runnable(){ 
      public void run() throws Exception { 
       throw new Exception(new NullPointerException()); 
      } 
     }, new Class[]{ NullPointerException.class }); 
    } 
} 

यह आपको एक ही बायलर प्लेट कोड फिर से लेखन की बचत होती है एक स्थिर सहायक समारोह एक समारोह शरीर और उम्मीद अपवाद की एक सरणी लेता है और फिर।

+1

यह अच्छा होगा, अगर जावा बंद हो गया था! जैसा है, कोशिश करें/पकड़ें/प्राप्त करें() शायद अज्ञात वर्गों को क्राफ्ट करने से कम बॉयलर प्लेट कोड है! –

7

आप JUnit आप के नवीनतम संस्करण उपयोग कर रहे हैं

ExtendedTestRunner.java (आज़माएं/कैच ब्लॉक में अपने तरीकों में से प्रत्येक रैप करने के लिए बिना) डिफ़ॉल्ट परीक्षण धावक आप के लिए इस संभाल करने के लिए विस्तार कर सकते हैं - नए परीक्षण धावक: -

public class ExtendedTestRunner extends BlockJUnit4ClassRunner 
{ 
    public ExtendedTestRunner(Class<?> clazz) 
     throws InitializationError 
    { 
     super(clazz); 
    } 

    @Override 
    protected Statement possiblyExpectingExceptions(FrameworkMethod method, 
                Object test, 
                Statement next) 
    { 
     ExtendedTest annotation = method.getAnnotation(ExtendedTest.class); 
     return expectsCauseException(annotation) ? 
       new ExpectCauseException(next, getExpectedCauseException(annotation)) : 
       super.possiblyExpectingExceptions(method, test, next); 
    } 

    @Override 
    protected List<FrameworkMethod> computeTestMethods() 
    { 
     Set<FrameworkMethod> testMethods = new HashSet<FrameworkMethod>(super.computeTestMethods()); 
     testMethods.addAll(getTestClass().getAnnotatedMethods(ExtendedTest.class)); 
     return testMethods; 
    } 

    @Override 
    protected void validateTestMethods(List<Throwable> errors) 
    { 
     super.validateTestMethods(errors); 
     validatePublicVoidNoArgMethods(ExtendedTest.class, false, errors); 
    } 

    private Class<? extends Throwable> getExpectedCauseException(ExtendedTest annotation) 
    { 
     if (annotation == null || annotation.expectedCause() == ExtendedTest.None.class) 
      return null; 
     else 
      return annotation.expectedCause(); 
    } 

    private boolean expectsCauseException(ExtendedTest annotation) { 
     return getExpectedCauseException(annotation) != null; 
    } 

} 

ExtendedTest.java टिप्पणी के साथ परीक्षण तरीकों चिह्नित करने के लिए:

@Retention(RetentionPolicy.RUNTIME) 
@Target({ElementType.METHOD}) 
public @interface ExtendedTest 
{ 

    /** 
    * Default empty exception 
    */ 
    static class None extends Throwable { 
     private static final long serialVersionUID= 1L; 
     private None() { 
     } 
    } 

    Class<? extends Throwable> expectedCause() default None.class; 
} 

ExpectCauseException.java - नई JUnit कथन:

public class ExpectCauseException extends Statement 
{ 
    private Statement fNext; 
    private final Class<? extends Throwable> fExpected; 

    public ExpectCauseException(Statement next, Class<? extends Throwable> expected) 
    { 
     fNext= next; 
     fExpected= expected; 
    } 

    @Override 
    public void evaluate() throws Exception 
    { 
     boolean complete = false; 
     try { 
      fNext.evaluate(); 
      complete = true; 
     } catch (Throwable e) { 
      if (e.getCause() == null || !fExpected.isAssignableFrom(e.getCause().getClass())) 
      { 
       String message = "Unexpected exception cause, expected<" 
          + fExpected.getName() + "> but was<" 
          + (e.getCause() == null ? "none" : e.getCause().getClass().getName()) + ">"; 
       throw new Exception(message, e); 
      } 
     } 
     if (complete) 
      throw new AssertionError("Expected exception cause: " 
        + fExpected.getName()); 
    } 
} 

उपयोग:

@RunWith(ExtendedTestRunner.class) 
public class MyTests 
{ 
    @ExtendedTest(expectedCause = MyException.class) 
    public void someMethod() 
    { 
     throw new RuntimeException(new MyException()); 
    } 
} 
+0

मुझे इस समाधान से प्यार है! हालांकि, दुख की बात है कि मुझे ग्रोवी जुनीट 4 परीक्षण के संयोजन के साथ संकलन करने में परेशानी हो रही है। –

+0

यह सबसे साफ समाधान है। संपादन के युगल हालांकि: विस्तारितस्टारनर को स्प्रिंग संदर्भ का सही समर्थन करने के लिए SpringJUnit4ClassRunner का विस्तार करने की आवश्यकता है। इसके अलावा computeTestMethods में असंगत वापसी प्रकार है (ArrayList होना चाहिए)। – warden

4

आप अपवादों के लिए एक Matcher बना सकते हैं। यह तब भी काम करता है जब आप Arquillian के @RunWith(Arquillian.class) जैसे किसी अन्य परीक्षण धावक का उपयोग कर रहे हैं ताकि आप ऊपर दिए गए @RunWith(ExtendedTestRunner.class) दृष्टिकोण का उपयोग न कर सकें।@Rule और ExpectedException इस तरह से इसका इस्तेमाल

public class ExceptionMatcher extends BaseMatcher<Object> { 
    private Class<? extends Throwable>[] classes; 

    // @SafeVarargs // <-- Suppress warning in Java 7. This usage is safe. 
    public ExceptionMatcher(Class<? extends Throwable>... classes) { 
     this.classes = classes; 
    } 

    @Override 
    public boolean matches(Object item) { 
     for (Class<? extends Throwable> klass : classes) { 
      if (! klass.isInstance(item)) { 
       return false; 
      } 

      item = ((Throwable) item).getCause(); 
     } 

     return true; 
    } 

    @Override 
    public void describeTo(Description descr) { 
     descr.appendText("unexpected exception"); 
    } 
} 

तब:

यहाँ एक सरल उदाहरण है

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

@Test 
public void testSomething() { 
    thrown.expect(new ExceptionMatcher(IllegalArgumentException.class, IllegalStateException.class)); 

    throw new IllegalArgumentException("foo", new IllegalStateException("bar")); 
} 

क्रेग रिंगर द्वारा जोड़ा गया में 2012 संपादित करें: एक उन्नत और अधिक विश्वसनीय संस्करण:

  • मूल यूएसए
  • से अपरिवर्तित जी बेजोड़ अपवाद फेंकने के लिए वैकल्पिक 1 तर्क boolean rethrow पास कर सकते हैं। यह आसान डिबगिंग के लिए नेस्टेड अपवादों के स्टैक ट्रेस को संरक्षित करता है।
  • उपयोग Apache Commons Lang अपवाद कारण लूप को संभालने और कुछ सामान्य अपवाद वर्गों द्वारा उपयोग किए जाने वाले गैर-मानक अपवाद घोंसले को संभालने के लिए उपयोग करता है।
  • स्व वर्णन अपवाद स्वीकार किए जाते हैं शामिल विफलता पर
  • स्व वर्णन अपवाद का एक कारण ढेर का सामना करना पड़ा
  • हैंडल जावा 7 चेतावनी भी शामिल है। पुराने संस्करणों पर @SaveVarargs हटाएं।

पूर्ण कोड:

import org.apache.commons.lang3.exception.ExceptionUtils; 
import org.hamcrest.BaseMatcher; 
import org.hamcrest.Description; 


public class ExceptionMatcher extends BaseMatcher<Object> { 
    private Class<? extends Throwable>[] acceptedClasses; 

    private Throwable[] nestedExceptions; 
    private final boolean rethrow; 

    @SafeVarargs 
    public ExceptionMatcher(Class<? extends Throwable>... classes) { 
     this(false, classes); 
    } 

    @SafeVarargs 
    public ExceptionMatcher(boolean rethrow, Class<? extends Throwable>... classes) { 
     this.rethrow = rethrow; 
     this.acceptedClasses = classes; 
    } 

    @Override 
    public boolean matches(Object item) { 
     nestedExceptions = ExceptionUtils.getThrowables((Throwable)item); 
     for (Class<? extends Throwable> acceptedClass : acceptedClasses) { 
      for (Throwable nestedException : nestedExceptions) { 
       if (acceptedClass.isInstance(nestedException)) { 
        return true; 
       } 
      } 
     } 
     if (rethrow) { 
      throw new AssertionError(buildDescription(), (Throwable)item); 
     } 
     return false; 
    } 

    private String buildDescription() { 
     StringBuilder sb = new StringBuilder(); 
     sb.append("Unexpected exception. Acceptable (possibly nested) exceptions are:"); 
     for (Class<? extends Throwable> klass : acceptedClasses) { 
      sb.append("\n "); 
      sb.append(klass.toString()); 
     } 
     if (nestedExceptions != null) { 
      sb.append("\nNested exceptions found were:"); 
      for (Throwable nestedException : nestedExceptions) { 
       sb.append("\n "); 
       sb.append(nestedException.getClass().toString()); 
      } 
     } 
     return sb.toString(); 
    } 

    @Override 
    public void describeTo(Description description) { 
     description.appendText(buildDescription()); 
    } 

} 

ठेठ उत्पादन:

java.lang.AssertionError: Expected: Unexpected exception. Acceptable (possibly nested) exceptions are: 
    class some.application.Exception 
Nested exceptions found were: 
    class javax.ejb.EJBTransactionRolledbackException 
    class javax.persistence.NoResultException 
    got: <javax.ejb.EJBTransactionRolledbackException: getSingleResult() did not retrieve any entities.> 
+0

उत्कृष्ट और बहुत उपयोगी उत्तर - धन्यवाद। EJBs का परीक्षण करने के लिए आर्किलीयन के साथ काम करते समय यह दृष्टिकोण एक lifesaver है, क्योंकि वे एक EJBException में प्रत्येक अनचेक अपवाद लपेटना पसंद करते हैं। –

+0

मैंने उत्तर में उदाहरण को और अधिक पूर्ण में बढ़ा दिया है। –

1

सबसे संक्षिप्त वाक्य रचना catch-exception द्वारा प्रदान की जाती है:

import static com.googlecode.catchexception.CatchException.*; 

catchException(myObj).doSomethingNasty(); 
assertTrue(caughtException().getCause() instanceof MyException); 
66

JUnit 4.11 के रूप में आप उपयोग कर सकते हैं ExpectedException नियम का expectCause() विधि:

import static org.hamcrest.CoreMatchers.*; 

// ... 

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

@Test 
public void throwsNestedException() throws Exception { 
    expectedException.expectCause(isA(SomeNestedException.class)); 

    throw new ParentException("foo", new SomeNestedException("bar")); 
} 
+6

हैमक्रिस्ट जेनेरिक नरक की वजह से, लाइन 6 को इस तरह दिखना है: 'अपेक्षित एक्सेप्शन.एक्सपेक्ट कॉज़ (है (IsInstanceOf। instanceOf (SomeNestedException.class));' लेकिन इसके अलावा, यह एक सुरुचिपूर्ण समाधान है। – thrau

+1

@ thrau का समाधान अतिरिक्त के बिना मेरे लिए काम करता है: 'अपेक्षित एक्सेप्शन.एक्सपेक्ट कॉज़ (IsInstanceOf। instanceOf (SomeNestedE xception.class));' – Jardo

+0

@Jardo हाँ, यह सही है - है() केवल सिंटैक्टिक चीनी है जो गुजरती है नेस्टेड matcher। यहां दस्तावेज़ देखें: http://hamcrest.org/JavaHamcrest/javadoc/1.3/org/hamcrest/core/Is.html#is(org.hamcrest।Matcher) – Rowan

संबंधित मुद्दे