सामग्री पर जाएँ
Topos Uranos

सारांश

जो प्रोग्राम कंपाइल हो जाता है और कुछ उचित-सा लिख देता है, वह केवल इसी कारण परीक्षित नहीं हो जाता। यह पाठ विधिपूर्वक परीक्षण करना सिखाता है: विनिर्देश से, प्रदेशों और सीमाओं के अनुसार, मामले निकालना; उन्हें एक स्वचालित परीक्षण प्रोग्राम में इकट्ठा करना जो विफलताओं को गिनता है और उनकी सूचना देता है; assert से वह जाँचना जो प्रोग्राम सही होने पर घटित नहीं हो सकता, और static_assert से, कंपाइल के समय, वह जाँचना जो किसी constexpr फ़ंक्शन को गणना करके देना चाहिए; और परीक्षणों को डिटेक्टरों के साथ चलाना, जो उस अपरिभाषित व्यवहार को पकड़ते हैं जिसे एक सही परिणाम छिपा देता है। यह सब एक चेतावनी पर टिका है: परीक्षण किसी त्रुटि की उपस्थिति दिखा सकता है, उसकी अनुपस्थिति कभी नहीं।

अधिगम उद्देश्य

  1. किसी फ़ंक्शन के विनिर्देश से परीक्षण मामले निकालना: उसके इनपुट के खंडन, उनके बीच की सीमाएँ और विशेष स्थितियाँ, और हर एक में पश्च-शर्त जो परिणाम माँगती है वह भी।
  2. ऐसा स्वचालित परीक्षण प्रोग्राम लिखना जो प्राप्त परिणामों की अपेक्षित परिणामों से तुलना करे, बिना रुके हर विफलता की सूचना दे, और अंत में एक सारांश तथा उसे दर्शाने वाले एग्ज़िट कोड के साथ समाप्त हो।
  3. पूर्व-शर्तों और निश्चरों की जाँच के लिए assert का उपयोग करना, और समझाना कि -DNDEBUG के साथ कंपाइल करने पर वह क्यों ग़ायब हो जाता है और उसकी शर्त में साइड इफ़ेक्ट क्यों नहीं होने चाहिए।
  4. कंपाइल के समय परिणामों का परीक्षण करने के लिए static_assert का उपयोग constexpr फ़ंक्शनों के साथ करना, और उस अपरिभाषित व्यवहार को पकड़ने के लिए जिसे परिणाम प्रकट नहीं करते, परीक्षणों को डिटेक्टरों के साथ चलाना।

परीक्षण करना क्या है

किसी प्रोग्राम का परीक्षण करना उसे चुने हुए इनपुट के साथ चलाना और जो वह करता है उसकी तुलना उससे करना है जो उसे करना चाहिए था। हर परीक्षण मामला दो भागों से बनता है: एक इनपुट जो पूर्व-शर्त को पूरा करता है, और वह परिणाम जो पश्च-शर्त उस इनपुट के लिए माँगती है, पहले से और किसी दूसरे रास्ते से निकाला हुआ (हाथ से, किसी दूसरे सूत्र से, किसी ज्ञात तालिका से)। अपेक्षित परिणाम के बिना परीक्षण नहीं होता, केवल एक बार चलाना होता है: आउटपुट देखकर उसे “उचित” पाना एक धारणा है, तुलना नहीं।

पर int के एक आर्ग्युमेंट वाला फ़ंक्शन चार अरब से अधिक इनपुट स्वीकार करता है, और दो आर्ग्युमेंट वाला फ़ंक्शन लगभग 1.8⋅10191.8 \cdot 10^{19} युग्म; परीक्षणों का कोई भी समूह उन सबसे होकर नहीं गुज़रता। यदि ऐसा है, तो परीक्षण संभव मामलों का एक नगण्य अंश ही जाँचते हैं, और सबके सफल होने से यह निष्कर्ष नहीं निकलता कि फ़ंक्शन सही है। एड्सगर डाइक्स्ट्रा ने 1970 में यह बात इस तरह रखी: परीक्षण त्रुटियों की उपस्थिति दिखा सकते हैं, उनकी अनुपस्थिति कभी नहीं। यह वही भेद है जो पाठ्यक्रम “गणितीय तर्कशास्त्र I” मामलों को जाँचने और सिद्ध करने के बीच करता है: यह जाँच लेना कि कोई सूत्र कुछ मानों के लिए सत्य है, यह सिद्ध नहीं करता कि वह सभी के लिए सत्य है, जबकि एक प्रमाण (इस पाठ्यक्रम में, पाठ सही लूप की रचना का निश्चर) सभी मामलों को एक साथ ढक लेता है। परीक्षण उस विवेचन की जगह नहीं लेते: वे उन त्रुटियों को खोजते हैं जो उससे छूट गईं।

इससे मामलों को चुनने की कसौटी निकलती है। यदि परीक्षण शुद्धता की पुष्टि नहीं कर सकते, तो उनका मूल्य त्रुटियाँ खोजने में है, और अच्छा परीक्षण मामला वह है जिसके विफल होने की संभावना सबसे अधिक हो। जिस मामले को लेखक ने इसलिए चुना कि उसे पता था कि वह चल जाएगा, वह कुछ नहीं बताता। आगे का प्रोग्राम इसे एक ऐसे फ़ंक्शन से दिखाता है जिसे किसी अरिक्त वेक्टर का सबसे बड़ा अवयव लौटाना चाहिए।

C++ प्रोग्राम
#include <print>#include <vector>​// आँकड़ों की एक सूची का सबसे बड़ा अवयव।// पूर्व-शर्त: values रिक्त नहीं है।// पश्च-शर्त: values का सबसे बड़ा अवयव लौटाता है।int largest(const std::vector<int>& values){    int best{0};    for (const int v : values) {        if (v > best) {            best = v;        }    }    return best;}​int main(){    std::println("{}", largest({3, 9, 4}));    std::println("{}", largest({7}));    std::println("{}", largest({1, 2, 3, 4}));    std::println("{}", largest({-5, -2, -8}));}
आउटपुट
9740

पहले तीन मामले वे हैं जिन्हें सब लोग सबसे पहले लिखते हैं, और फ़ंक्शन उनमें सफल होता है: 9, 7 और 4 सही हैं। चौथा 0 देता है, जो एक ऐसा मान है जो वेक्टर में है ही नहीं, जबकि उसे −2-2 देना चाहिए था। त्रुटि पंक्ति 9 में है: best पहले अवयव से नहीं, 0 से शुरू होता है, इसलिए फ़ंक्शन तभी सही है जब कोई अवयव धनात्मक या शून्य हो। सामान्य मामलों में यह गुण साझा था, बिना किसी के उसे चुने; चौथा मामला जान-बूझकर उसका उल्लंघन करता है। सुधार है int best{values[0]};, जिसे पूर्व-शर्त वैध बनाती है, क्योंकि वह गारंटी देती है कि values[0] मौजूद है।