2010-02-26 26 views
19

मैंने GLib दस्तावेज में खोदना शुरू कर दिया है और पाया है कि यह एक इकाई परीक्षण ढांचा भी प्रदान करता है।सादा सी में यूनिट परीक्षण कैसे लिखें?

लेकिन आप प्रक्रियात्मक भाषा में यूनिट परीक्षण कैसे कर सकते हैं? या सी में प्रोग्राम ओओ प्रोग्राम करने की आवश्यकता है?

उत्तर

19

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

अधिक परिष्कृत परीक्षण, जैसे कि मैक्स या स्टब्स का उपयोग भी संभव है, लेकिन यह लगभग गतिशील भाषाओं में या लगभग ऑब्जेक्ट उन्मुख भाषाओं जैसे सी ++ जैसी आसान नहीं है। इस दृष्टिकोण के लिए #defines का उपयोग करने का एक तरीका है। इसका एक उदाहरण यह आलेख है, Unit testing OpenGL applications, जो दिखाता है कि ओपनजीएल कॉल का नकल कैसे करें। यह आपको परीक्षण करने की अनुमति देता है कि ओपनजीएल कॉल के वैध अनुक्रम बनाए गए हैं।

एक और विकल्प कमजोर प्रतीकों का लाभ उठाना है।उदाहरण के लिए, सभी एमपीआई एपीआई फ़ंक्शन कमजोर प्रतीक होते हैं, इसलिए यदि आप अपने स्वयं के एप्लिकेशन में एक ही प्रतीक को परिभाषित करते हैं, तो आपका कार्यान्वयन पुस्तकालय में कमजोर कार्यान्वयन को ओवरराइड करता है। यदि लाइब्रेरी में प्रतीकों कमजोर नहीं थे, तो आपको लिंक समय पर डुप्लिकेट प्रतीक त्रुटियां मिलेंगी। इसके बाद आप संपूर्ण एमपीआई सी एपीआई का प्रभावी रूप से नकल कर सकते हैं, जो आपको यह सुनिश्चित करने की अनुमति देता है कि कॉल ठीक से मेल खाते हैं और कोई अतिरिक्त कॉल नहीं है जो डेडलॉक्स का कारण बन सकती है। dlopen() और dlsym() का उपयोग करके पुस्तकालय के कमजोर प्रतीकों को लोड करना भी संभव है, और यदि आवश्यक हो तो कॉल पास करें। एमपीआई वास्तव में पीएमपीआई प्रतीकों को प्रदान करता है, जो मजबूत हैं, इसलिए dlopen() और दोस्तों का उपयोग करना आवश्यक नहीं है।

आप सी के लिए यूनिट परीक्षण के कई लाभों को महसूस कर सकते हैं। यह थोड़ा कठिन है, और रूबी या जावा में लिखे गए किसी भी स्तर से कवरेज के समान स्तर को प्राप्त करना संभव नहीं है, लेकिन यह निश्चित रूप से लायक है करते हुए।

+3

नहीं हैं। आप यह भी दिखाते हैं कि सी में निर्भरता इंजेक्शन की व्यावहारिक आवश्यकता को कैसे संभालना है, जो वास्तविक इकाई-परीक्षण प्राप्त करने के लिए कोड-पहुंच को अलग करने की प्राथमिक तकनीक है। – kirakun

11

सबसे बुनियादी स्तर पर, इकाई परीक्षण कोड का सिर्फ बिट्स कि कोड के अन्य बिट्स निष्पादित और आपको बताती हैं कि वे उम्मीद के रूप में काम कर रहे हैं।

आप मुख्य() फ़ंक्शन के साथ बस एक नया कंसोल ऐप बना सकते हैं, जिसने परीक्षण कार्यों की एक श्रृंखला को निष्पादित किया। प्रत्येक परीक्षण आपके ऐप में एक फ़ंक्शन कॉल करेगा और सफलता के लिए 0 या विफलता के लिए एक और मान वापस करेगा।

मैं आपको कुछ उदाहरण कोड दूंगा, लेकिन मैं वास्तव में सी के साथ जंगली हूं। मुझे यकीन है कि वहाँ कुछ ढांचे हैं जो इसे थोड़ा आसान बना देंगे।

+4

होगा आप अभी भी एक ढांचे के लिए देख रहे हैं उपलब्ध सी इकाई परीक्षण चौखटे लिए इस सूची कोशिश: http : //en.wikipedia.org/wiki/List_of_unit_testing_frameworks#C – tobsen

+0

आह-हा उत्कृष्ट सूची। मैंने सोचा कि वहां से कहीं और ढांचे हैं। –

1

मैं जोर का उपयोग करता हूं। हालांकि यह वास्तव में एक ढांचा नहीं है।

+4

जोर परीक्षण के लिए बहुत अच्छा नहीं है, क्योंकि आपका परीक्षण कार्यक्रम पहली विफलता पर समाप्त हो जाएगा, जो स्वचालित परीक्षण और परीक्षण रिपोर्टिंग को गंभीर रूप से सीमित करता है। –

+0

आर्ट का उपयोग इकाई परीक्षण के हिस्से के रूप में किया जा सकता है या यह सुनिश्चित करने के एक हिस्से के रूप में किया जा सकता है कि आपके कार्यों को ठीक से कहा जा रहा है, लेकिन वे "यूनिट परीक्षण" –

3

यूनिट परीक्षण करने का सबसे आसान तरीका एक साधारण ड्राइवर कोड बनाना है जो दूसरे कोड से जुड़ा हुआ है, और प्रत्येक मामले में प्रत्येक फ़ंक्शन को कॉल करें ... और कार्यों के परिणामों के मूल्यों का दावा करें और बनाएं थोड़ा करके बिट ... कि मैं इसे कैसे वैसे भी करना

 
int main(int argc, char **argv){ 

    // call some function 
    int x = foo(); 

    assert(x > 1); 

    // and so on.... 

} 

आशा इस मदद करता है, सादर, टॉम है।

4

कुछ भी नहीं आंतरिक रूप से अलगाव में कोड के छोटे टुकड़े का परीक्षण के बारे में वस्तु उन्मुख नहीं है। प्रक्रियात्मक भाषाओं में आप कार्यों और संग्रह का परीक्षण करते हैं।

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

Dave's Unit Test न्यूनतम दखल है अभी तक यह कर सकते हैं कुछ परीक्षण मैं मूल रूप से सोचा था संभव एक पूर्वप्रक्रमक आधारित ढांचे के लिए (आप मांग कर सकते हैं कि कोड की एक निश्चित खिंचाव, कुछ शर्तों के अधीन एक विभाजन गलती फेंक नहीं होगा और यह परीक्षण होगा तुम्हारे लिए)।

यह भी एक उदाहरण है कि क्यों प्रीप्रोसेसर का भारी उपयोग हार्ड सुरक्षित रूप से करने के लिए है।

1

सी के साथ इसे मौजूदा कोड के शीर्ष पर एक ढांचे को लागू करने से आगे जाना चाहिए।

एक चीज जो मैंने हमेशा की है वह एक परीक्षण मॉड्यूल (मुख्य के साथ) है कि आप अपने कोड का परीक्षण करने के लिए छोटे परीक्षण चला सकते हैं। यह आपको कोड और परीक्षण चक्रों के बीच बहुत छोटी वृद्धि करने की अनुमति देता है।

बड़ी चिंता आपके कोड को टेस्ट करने योग्य लिख रही है। छोटे, स्वतंत्र कार्यों पर ध्यान केंद्रित करें जो साझा चर या राज्य पर भरोसा नहीं करते हैं। एक "कार्यात्मक" तरीके (राज्य के बिना) में लिखने का प्रयास करें, यह परीक्षण करना आसान होगा। यदि आपके पास निर्भरता है जो हमेशा वहां नहीं हो सकती है या धीमी है (डेटाबेस की तरह), तो आपको एक संपूर्ण "नकली" परत लिखनी पड़ सकती है जिसे परीक्षण के दौरान आपके डेटाबेस के लिए प्रतिस्थापित किया जा सकता है।

सिद्धांत इकाई परीक्षण लक्ष्यों अभी भी लागू होते हैं: परीक्षण के अंतर्गत कोड सुनिश्चित हमेशा किसी दिए गए राज्य के लिए रीसेट करता है, परीक्षण लगातार, आदि ...

जब मैं सी (वापस विंडोज से पहले) में कोड लिखा था मैं एक बैच था फ़ाइल जो एक संपादक लाएगी, तब जब मुझे संपादन और बाहर किया गया था, तो यह संकलन, लिंक, परीक्षण निष्पादित करेगा और फिर संपादक को बिल्ड परिणाम, परीक्षण परिणाम और विभिन्न विंडो में कोड के साथ लाएगा। मेरे ब्रेक के बाद (संकलित किए जा रहे कार्यों के आधार पर कई घंटों तक एक मिनट) मैं केवल परिणामों की समीक्षा कर सकता हूं और संपादन पर सीधे वापस जा सकता हूं। मुझे यकीन है कि इन दिनों इन प्रक्रियाओं में सुधार किया जा सकता है :)

6

आप libtap का उपयोग कर सकते हैं जो कई कार्यों को प्रदान करता है जो परीक्षण विफल होने पर डायग्नोस्टिक्स प्रदान कर सकते हैं। इसके उपयोग का एक उदाहरण:

#include <mystuff.h> 
#include <tap.h> 

int main() { 
    plan(3); 
    ok(foo(), "foo returns 1"); 
    is(bar(), "bar", "bar returns the string bar"); 
    cmp_ok(baz(), ">", foo(), "baz returns a higher number than foo"); 
    done_testing; 
} 

यह अन्य भाषाओं में टैप पुस्तकालयों के समान है।

3

यहां एक उदाहरण दिया गया है कि आप किसी दिए गए फ़ंक्शन के लिए एक परीक्षण प्रोग्राम में एकाधिक परीक्षण कैसे कार्यान्वित करेंगे जो लाइब्रेरी फ़ंक्शन को कॉल कर सकता है।

#include <stdlib.h> 

int my_div(int x, int y) 
{ 
    if (y==0) exit(2); 
    return x/y; 
} 

हम उसके बाद निम्न परीक्षण कार्यक्रम बनाने के लिए::

#include <stdio.h> 
#include <stdlib.h> 
#include <unistd.h> 
#include <setjmp.h> 

// redefine assert to set a boolean flag 
#ifdef assert 
#undef assert 
#endif 
#define assert(x) (rslt = rslt && (x)) 

// the function to test 
int my_div(int x, int y); 

// main result return code used by redefined assert 
static int rslt; 

// variables controling stub functions 
static int expected_code; 
static int should_exit; 
static jmp_buf jump_env; 

// test suite main variables 
static int done; 
static int num_tests; 
static int tests_passed; 

// utility function 
void TestStart(char *name) 
{ 
    num_tests++; 
    rslt = 1; 
    printf("-- Testing %s ... ",name); 
} 

// utility function 
void TestEnd() 
{ 
    if (rslt) tests_passed++; 
    printf("%s\n", rslt ? "success" : "fail"); 
} 

// stub function 
void exit(int code) 
{ 
    if (!done) 
    { 
     assert(should_exit==1); 
     assert(expected_code==code); 
     longjmp(jump_env, 1); 
    } 
    else 
    { 
     _exit(code); 
    } 
} 

// test case 
void test_normal() 
{ 
    int jmp_rval; 
    int r; 

    TestStart("test_normal"); 
    should_exit = 0; 
    if (!(jmp_rval=setjmp(jump_env))) 
    { 
     r = my_div(12,3); 
    } 

    assert(jmp_rval==0); 
    assert(r==4); 
    TestEnd(); 
} 

// test case 
void test_div0() 
{ 
    int jmp_rval; 
    int r; 

    TestStart("test_div0"); 
    should_exit = 1; 
    expected_code = 2; 
    if (!(jmp_rval=setjmp(jump_env))) 
    { 
     r = my_div(2,0); 
    } 

    assert(jmp_rval==1); 
    TestEnd(); 
} 

int main() 
{ 
    num_tests = 0; 
    tests_passed = 0; 
    done = 0; 
    test_normal(); 
    test_div0(); 
    printf("Total tests passed: %d\n", tests_passed); 
    done = 1; 
    return !(tests_passed == num_tests); 
} 

assert को पुनर्परिभाषित एक बूलियन चर अद्यतन करने के लिए रखकर आप पर अगर एक जारी रख सकते हैं

मान लीजिए हम निम्नलिखित मॉड्यूल का परीक्षण करना चाहते दावा कितना सफल हुआ और कितने असफल रहा, इसका ट्रैक रखते हुए, कई परीक्षणों में विफल रहता है और चलाता है।

प्रत्येक परीक्षण की शुरुआत में, rslt (मैक्रो द्वारा उपयोग किए जाने वाले चर) को 1 पर सेट करें, और अपने स्टब फ़ंक्शंस को नियंत्रित करने वाले किसी भी चर सेट करें।यदि आपके स्टब्स में से एक बार एक से अधिक बार बुलाया जाता है, तो आप नियंत्रण चर के सरणी सेट कर सकते हैं ताकि स्टब्स विभिन्न कॉलों पर विभिन्न स्थितियों की जांच कर सकें।

चूंकि कई लाइब्रेरी फ़ंक्शन कमजोर प्रतीक हैं, इसलिए उन्हें आपके परीक्षण कार्यक्रम में फिर से परिभाषित किया जा सकता है ताकि उन्हें इसके बजाय बुलाया जा सके। फ़ंक्शन को परीक्षण करने के लिए कॉल करने से पहले, आप स्टब फ़ंक्शन के व्यवहार को नियंत्रित करने और फ़ंक्शन पैरामीटर पर स्थितियों की जांच करने के लिए कई राज्य चर सेट कर सकते हैं।

ऐसे मामलों में जहां आप इसे फिर से परिभाषित नहीं कर सकते हैं, स्टब फ़ंक्शन को एक अलग नाम दें और परीक्षण के लिए कोड में प्रतीक को दोबारा परिभाषित करें। उदाहरण के लिए, यदि आप fopen स्टब करना चाहते हैं लेकिन पाते हैं कि यह एक कमजोर प्रतीक नहीं है, तो अपने स्टब को my_fopen के रूप में परिभाषित करें और फ़ाइल को -Dfopen=my_fopen के साथ परीक्षण करने के लिए संकलित करें।

इस विशेष मामले में, परीक्षण करने के लिए फ़ंक्शन exit पर कॉल कर सकता है। यह मुश्किल है, क्योंकि exit परीक्षण किए जा रहे फ़ंक्शन पर वापस नहीं आ सकता है। यह दुर्लभ समय में से एक है जब setjmp और longjmp का उपयोग करना समझ में आता है। फ़ंक्शन में फ़ंक्शन दर्ज करने से पहले setjmp का उपयोग करें, फिर exit पर स्टब किए गए आप longjmp पर सीधे अपने परीक्षण मामले पर वापस लौटने के लिए कॉल करें।

यह भी ध्यान रखें कि पुन: परिभाषित exit में एक विशेष चर है कि यह देखने के लिए जांचता है कि क्या आप वास्तव में प्रोग्राम से बाहर निकलना चाहते हैं और ऐसा करने के लिए _exit पर कॉल करें। यदि आप ऐसा नहीं करते हैं, तो आपका परीक्षण प्रोग्राम साफ़ रूप से नहीं छोड़ा जा सकता है।

यह परीक्षण सूट भी प्रयास किए गए और असफल परीक्षणों की संख्या की गणना करता है और यदि सभी परीक्षण पास हुए और 1 अन्यथा 0 लौटाता है। इस तरह, make परीक्षण विफलताओं की जांच कर सकते हैं और तदनुसार कार्य कर सकते हैं।

ऊपर परीक्षण कोड इच्छा उत्पादन निम्नलिखित:

-- Testing test_normal ... success 
-- Testing test_div0 ... success 
Total tests passed: 2 

और वापसी कोड 0.

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