परिचय: "मेरा टेस्ट क्यों फेल हो रहा है जबकि उसे पास होना चाहिए?"
\n\nक्या आपने कभी कोई यूनिट टेस्ट लिखा है जो exception को पकड़ने वाला था, लेकिन वह लाल एरर के साथ फेल हो गया? परिचित लग रहा है? Stack Overflow पर सवाल "How do you assert that a certain exception is thrown in JUnit tests?" को हज़ारों व्यूज़ और सैकड़ों कमेंट्स मिले हैं। और यह कोई संयोग नहीं है: exception testing उन विषयों में से एक है जहां अनुभवी डेवलपर्स भी कभी-कभी फंस जाते हैं।
\n\n2025 में, जब कोड की गुणवत्ता एक महत्वपूर्ण कारक बन गई है (खासकर माइक्रोसर्विसेज़ और AI पाइपलाइनों में), exceptions को सही तरीके से चेक करना सिर्फ "अच्छा व्यवहार" नहीं, बल्कि एक बुनियादी कौशल है। इस आर्टिकल में हम JUnit 5 में exception assert करने के सभी मौजूदा तरीकों को समझेंगे, कमियां दिखाएंगे और बेस्ट प्रैक्टिसेज़ की एक चेकलिस्ट देंगे। चलिए शुरू करते हैं!
\n\nExceptions को टेस्ट करना क्यों ज़रूरी है?
\n\nसोचिए: आप एक मेथड divide(int a, int b) लिख रहे हैं। अगर b == 0 है, तो मेथड को ArithmeticException फेंकना चाहिए। इस व्यवहार के लिए टेस्ट न होने पर, आप जोखिम उठाते हैं कि एक महीने बाद कोई दूसरा डेवलपर (या आप खुद) गलती से चेक हटा दे, और एप्लिकेशन प्रोडक्शन में बिना समझे एरर के साथ क्रैश हो जाए।
Exceptions पर टेस्ट आपका बीमा हैं। वे बॉर्डरलाइन केस में सिस्टम के अपेक्षित व्यवहार को डॉक्यूमेंट करते हैं। और JUnit 5 इसके लिए कई शानदार टूल्स देता है।
\n\nतरीका 1: assertThrows — आधुनिक टेस्टिंग का राजा
\n\nJUnit 5 (और इसके पैकेज org.junit.jupiter.api) से शुरू होकर, assertThrows मेथड डी-फैक्टो स्टैंडर्ड बन गया है। क्यों? क्योंकि यह संक्षिप्त, पढ़ने में आसान और कार्यात्मक है।
import static org.junit.jupiter.api.Assertions.assertThrows;\n\n@Test\nvoid whenDivideByZero_thenArithmeticException() {\n assertThrows(ArithmeticException.class, \n () -> calculator.divide(10, 0));\n}\n\n\nयहां क्या हो रहा है? assertThrows अपेक्षित exception का क्लास और एक लैम्ब्डा लेता है जो उसे फेंकने वाला है। अगर exception फेंका जाता है — टेस्ट ग्रीन। अगर नहीं — रेड। सब कुछ सरल और स्पष्ट।
एडवांस्ड वर्जन: Exception का मैसेज चेक करना
\n\nकभी-कभी सिर्फ exception का टाइप चेक करना काफी नहीं होता — यह सुनिश्चित करना होता है कि मैसेज में सही टेक्स्ट है। assertThrows exception ऑब्जेक्ट लौटाता है, और आप इसके प्रॉपर्टीज़ चेक कर सकते हैं:
import static org.junit.jupiter.api.Assertions.*;\n\n@Test\nvoid whenInvalidEmail_thenExceptionWithMessage() {\n IllegalArgumentException exception = assertThrows(\n IllegalArgumentException.class,\n () -> userService.register("invalid-email")\n );\n \n assertTrue(exception.getMessage().contains("गलत ईमेल"));\n}\n\n\nयह तरीका खासतौर पर कस्टम exceptions के साथ फील्ड्स (जैसे एरर कोड) टेस्ट करने में उपयोगी है।
\n\nतरीका 2: पुराना और भरोसेमंद expected (JUnit 4 लीगेसी)
\n\nअगर आप JUnit 4 के साथ काम कर रहे हैं या लीगेसी कोड को सपोर्ट कर रहे हैं, तो आपने @Test एनोटेशन में expected एट्रीब्यूट ज़रूर देखा होगा:
@Test(expected = ArithmeticException.class)\npublic void whenDivideByZero_thenException() {\n calculator.divide(10, 0);\n}\n\n\nनुकसान: यह तरीका exception के मैसेज या उसके प्रॉपर्टीज़ को चेक नहीं कर सकता। इसके अलावा, अगर exception आपकी उम्मीद से पहले फेंका जाता है (जैसे इनिशियलाइज़ेशन लाइन में), तो टेस्ट गलत तरीके से पास हो सकता है। 2025 में नए कोड में इस तरीके से बचने की सलाह दी जाती है।
\n\nतरीका 3: Try-catch फ्लैग के साथ (कंट्रोल के शौकीनों के लिए)
\n\nJUnit 5 के आने से पहले, कई लोग try-catch और बूलियन फ्लैग का इस्तेमाल करते थे। यह भारी लगता है, लेकिन पूरा कंट्रोल देता है:
@Test\nvoid whenDivideByZero_thenExceptionCaught() {\n boolean thrown = false;\n try {\n calculator.divide(10, 0);\n } catch (ArithmeticException e) {\n thrown = true;\n }\n assertTrue(thrown);\n}\n\n\nयह तरीका अब भी काम करता है, लेकिन assertThrows ज्यादा साफ और कम कोड वाला है।
तरीका 4: JUnit 5 के @Rule और ExpectException (पुराना लेकिन काम का)
\n\nJUnit 4 में @Rule और ExpectedException का इस्तेमाल होता था। JUnit 5 में यह सीधे उपलब्ध नहीं है, लेकिन अगर आप JUnit 4 का इस्तेमाल कर रहे हैं, तो यह एक विकल्प है:
@Rule\npublic ExpectedException exceptionRule = ExpectedException.none();\n\n@Test\npublic void whenDivideByZero_thenException() {\n exceptionRule.expect(ArithmeticException.class);\n exceptionRule.expectMessage("/ by zero");\n calculator.divide(10, 0);\n}\n\n\nनोट: JUnit 5 में ExpectedException को हटा दिया गया है। इसलिए नए प्रोजेक्ट्स में assertThrows का ही इस्तेमाल करें।
तरीका 5: कस्टम एक्सेप्शन हैंडलर और एसर्शन
\n\nकभी-कभी आपको एक ही टेस्ट में कई exceptions को चेक करना होता है या कस्टम लॉजिक लागू करना होता है। ऐसे में आप अपना खुद का हैंडलर बना सकते हैं:
\n\n@FunctionalInterface\ninterface ExceptionAssertion {\n void assertException(Runnable runnable, Class extends Throwable> expected);\n}\n\n// इस्तेमाल:\nExceptionAssertion customAssert = (runnable, expected) -> {\n try {\n runnable.run();\n fail(\"कोई exception नहीं फेंका गया\");\n } catch (Exception e) {\n assertTrue(expected.isInstance(e));\n }\n};\n\n@Test\nvoid customExceptionTest() {\n customAssert.assertException(\n () -> calculator.divide(10, 0),\n ArithmeticException.class\n );\n}\n\n\nयह तरीका तब काम आता है जब आपको जटिल exception हैंडलिंग लॉजिक टेस्ट करना हो।
\n\n2025 में बेस्ट प्रैक्टिसेज़: चेकलिस्ट
\n\n- \n
- हमेशा assertThrows का इस्तेमाल करें — यह JUnit 5 का सबसे साफ और अनुशंसित तरीका है। \n
- एक टेस्ट में एक exception चेक करें — टेस्ट को सरल और फोकस्ड रखें। \n
- मैसेज और प्रॉपर्टीज़ चेक करें — सिर्फ टाइप नहीं, बल्कि exception का कंटेंट भी वेरिफाई करें। \n
- JUnit 4 के expected से बचें — यह पुराना और सीमित है। \n
- कस्टम exceptions के लिए अलग टेस्ट बनाएं — हर exception क्लास के लिए अलग टेस्ट मेथड बनाएं। \n
- नकारात्मक टेस्ट केस भी लिखें — जहां exception नहीं फेंका जाना चाहिए, वहां भी टेस्ट करें। \n
निष्कर्ष
\n\nException testing JUnit में एक ज़रूरी स्किल है जो आपके कोड को मजबूत और विश्वसनीय बनाता है। 2025 में assertThrows सबसे अच्छा तरीका है, लेकिन पुराने तरीकों को समझना भी ज़रूरी है ताकि लीगेसी कोड को हैंडल किया जा सके।
याद रखें: अच्छा टेस्ट वह है जो न सिर्फ सफलता के पथ को, बल्कि असफलता के किनारों को भी कवर करे। हैप्पी कोडिंग!