पैनिक — कोई सज़ा नहीं: Rust में आपातकालीन स्थितियों का परीक्षण क्यों करें?
\n\nकल्पना करें: आप Rust में पैसे के साथ काम करने के लिए एक लाइब्रेरी लिख रहे हैं। फ़ंक्शन divide_money(amount: u64, parts: u64) को पैनिक करना चाहिए यदि parts शून्य है — क्योंकि शून्य से भाग नहीं दिया जा सकता, और आप वास्तुशिल्प कारणों से Option या Result वापस नहीं करना चाहते। आप कैसे सुनिश्चित करेंगे कि यह पैनिक वास्तव में होती है? या आप एक पार्सर विकसित कर रहे हैं जो गलत डेटा देने पर आपातकालीन रूप से समाप्त होना चाहिए। पैनिक परीक्षणों के बिना, आप "शांत" बग्स का जोखिम उठाते हैं जो केवल प्रोडक्शन में दिखाई देंगे।
Rust में पैनिक का परीक्षण केवल "एक अच्छा विकल्प" नहीं है, बल्कि विश्वसनीय कोड बनाने के लिए एक महत्वपूर्ण उपकरण है। इस लेख में, हम समझेंगे कि unit-टेस्ट कैसे लिखें जो जाँचते हैं कि आपका कोड सही स्थितियों में वास्तव में पैनिक करता है। आप #[should_panic] मैक्रो, इसकी बारीकियों, std::panic::catch_unwind जैसे विकल्पों और सर्वोत्तम प्रथाओं के बारे में जानेंगे। चलिए शुरू करते हैं!
क्लासिक दृष्टिकोण: #[should_panic] मैक्रो
\n\nRust में पैनिक की जाँच करने का सबसे सरल और सामान्य तरीका #[should_panic] विशेषता का उपयोग करना है। इसे टेस्ट फ़ंक्शन के ऊपर रखा जाता है और यह कंपाइलर को बताता है: "यह टेस्ट केवल तभी सफल माना जाता है जब इसके अंदर पैनिक हुआ हो।"
मूल उदाहरण
\n\n// lib.rs या tests/unit_tests.rs\nfn divide(a: i32, b: i32) -> i32 {\n if b == 0 {\n panic!("शून्य से भाग!");\n }\n a / b\n}\n\n#[cfg(test)]\nmod tests {\n use super::*;\n\n #[test]\n #[should_panic]\n fn test_divide_by_zero() {\n divide(10, 0);\n }\n}\n\nटेस्ट को cargo test कमांड से चलाएँ — और आप हरा परिणाम देखेंगे। यदि पैनिक नहीं होती (उदाहरण के लिए, आप शून्य की जाँच हटा देते हैं), तो टेस्ट संदेश के साथ विफल हो जाएगा: "test test_divide_by_zero ... FAILED (expected to panic)"।
पैनिक के सटीक संदेश की जाँच
\n\nकभी-कभी केवल पैनिक के तथ्य की नहीं, बल्कि उसके विशिष्ट संदेश की भी पुष्टि करनी होती है। यह झूठी सकारात्मकता से बचने में मदद करता है, जब पैनिक किसी अन्य त्रुटि के कारण होती है। अतिरिक्त पैरामीटर expected का उपयोग करें:
#[test]\n#[should_panic(expected = "शून्य से भाग!")]\nfn test_divide_by_zero_with_message() {\n divide(10, 0);\n}\n\n// यह टेस्ट पास नहीं होगा — संदेश अलग है\n#[test]\n#[should_panic(expected = "शून्य से भाग!")]\nfn test_other_panic() {\n panic!("बिल्कुल अलग त्रुटि");\n}\n\nमहत्वपूर्ण: expected जाँचता है कि पैनिक संदेश में निर्दिष्ट स्ट्रिंग शामिल है (बिल्कुल बराबर नहीं होनी चाहिए)। यह लंबे संदेशों के लिए सुविधाजनक है।
उन्नत दृष्टिकोण: std::panic::catch_unwind
\n\nकभी-कभी #[should_panic] पर्याप्त नहीं होता। उदाहरण के लिए, यदि आप यह जाँचना चाहते हैं कि पैनिक किसी विशिष्ट स्थान पर हुई, न कि कहीं और, या यदि आपको एसिंक्रोनस कोड का परीक्षण करना है। ऐसे मामलों के लिए std::panic::catch_unwind मौजूद है — एक फ़ंक्शन जो पैनिक को पकड़ता है और इसे Result के रूप में लौटाता है।
यह कैसे काम करता है
\n\nuse std::panic;\n\nfn risky_function(should_panic: bool) {\n if should_panic {\n panic!("आपातकाल!");\n }\n}\n\n#[test]\nfn test_catch_panic() {\n let result = panic::catch_unwind(|| {\n risky_function(true);\n });\n assert!(result.is_err()); // जाँचें कि पैनिक हुई\n}\n\n#[test]\nfn test_no_panic() {\n let result = panic::catch_unwind(|| {\n risky_function(false);\n });\n assert!(result.is_ok()); // कोई पैनिक नहीं हुई\n}\n\nपैनिक के प्रकार की जाँच
\n\ncatch_unwind के साथ आप यह भी जाँच सकते हैं कि पैनिक में एक निश्चित मान है (केवल स्ट्रिंग नहीं):
use std::panic;\nuse std::any::Any;\n\nfn panic_with_number() {\n panic!(42); // हाँ, ऐसा संभव है — एक संख्या के साथ पैनिक!\n}\n\n#[test]\nfn test_panic_with_number() {\n let resu