2012-01-22 10 views
11

मैं सी पुस्तकालय के लिए परीक्षण लिखना चाहता हूं, सी में। मैं परीक्षण के लिए कुछ कार्यों को मजाक करना चाहता हूं।सी में फ़ंक्शन मॉकिंग (परीक्षण के लिए) सी?

मान लीजिए मेरे पुस्तकालय निम्नलिखित स्रोत से संकलित किया गया है:

/* foo.h */ 
int myfunction(int x, int y); 

/* foo.c */ 
#include "foo.h" 

static int square(int x) { return x * x; } 

int myfunction(int x, int y) { 
    return square(x) + square(y); 
} 

मैं इस तरह एक परीक्षण लिखना चाहते हैं:

/* foo_test.c */ 
#include "foo.h" 

static int square(int x) { return x + 1; } 

int main(void) { 
    assert(myfunction(0, 0) == 2); 
    return 0; 
} 

वहाँ किसी भी तरह से मैं इतना संकलन कर सकते हैं कि myfunction का उपयोग करेगा है की foo_test.c में foo.c में से एक की बजाय, केवल निष्पादन योग्य foo_test को लिंक करते समय? यही है, मैं foo.c को लाइब्रेरी में संकलित करना चाहता हूं (चलिए इसे libfoo.so पर कॉल करें), और उसके बाद को libfoo.so और कुछ जादू के साथ संकलित करना चाहते हैं ताकि मुझे निष्पादन योग्य foo_test मिलेगा जो square के विभिन्न कार्यान्वयन का उपयोग करता है।

square को static घोषित नहीं किया गया है, तो समाधान सुनना उपयोगी होगा, लेकिन उपरोक्त मामले को हल करना बेहतर होगा।

संपादित करें: यह निराशाजनक लगता है, लेकिन यहां एक विचार है: मान लीजिए कि मैं -O0 -g के साथ संकलित करता हूं, इसलिए यह संभावना नहीं है कि square इनलाइन हो जाएगा और मेरे पास प्रतीक दिखाना चाहिए कि कॉल कहां हल हो गया था। क्या ऑब्जेक्ट फ़ाइल में घुसने और हल किए गए संदर्भ को बदलने का कोई तरीका है?

+0

'एलडी_PRELOAD' आपको यह दे सकता है, लेकिन मुझे उम्मीद है कि किसी और के पास बेहतर जवाब होगा। – sarnold

+3

स्थिर कार्यों के लिए नहीं – bdonlan

+1

मुझे नहीं लगता कि 'LD_PRELOAD' ऐसा करने जा रहा है। मैं एक लाइब्रेरी के अंदर एक फ़ंक्शन (जो स्थैतिक हो सकता है) को बदलने में सक्षम होना चाहता हूं जो पहले ही संकलित है। जैसा कि मैं समझता हूं (और शायद यह झूठा है), 'foo_test' को जोड़ने का प्रयास करने से पहले' myfunction' के अंदर 'वर्ग' प्रतीक' को हल कर दिया जाएगा। – leif

उत्तर

4

ऐसा लगता है कि जीसीसी का उपयोग कर रहे हैं, ताकि आप कमजोर विशेषता का उपयोग कर सकते हैं:

कमजोर विशेषता घोषणा नहीं बल्कि एक वैश्विक से एक कमजोर प्रतीक के रूप में उत्सर्जित होना का कारण बनता है। यह मुख्य रूप से लाइब्रेरी फ़ंक्शंस को परिभाषित करने में उपयोगी है जिसे उपयोगकर्ता कोड, में ओवरराइड किया जा सकता है हालांकि यह गैर-फ़ंक्शन घोषणाओं के साथ भी उपयोग किया जा सकता है। कमजोर प्रतीकों ईएलएफ लक्ष्यों के लिए समर्थित हैं, और जीएनयू असेंबलर और लिंकर का उपयोग करते समय a.out लक्ष्यों के लिए भी।

http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html

+0

धन्यवाद। मैं शायद इसे यहां से ले जा सकता हूं, लेकिन क्या आप मुझे कमजोर संदर्भ को संशोधित करने के लिए कोई सुझाव दे सकते हैं? – leif

+1

बस एक ही नाम के साथ एक फ़ंक्शन को परिभाषित करें जो कमजोर नहीं है, फिर इसे लाइब्रेरी में कमजोर फ़ंक्शन को ओवरराइड करना चाहिए। –

4

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

0

आपके जैसे मामलों में मैं Typemock Isolator++ एपीआई का उपयोग करें।

यह आपको अपने कार्यान्वयन के द्वारा विधि के मूल व्यवहार को प्रतिस्थापित करने की अनुमति देता है। लेकिन, उचित भाषा विनिर्देशों के अनुसार, आपको परीक्षणों के लिए और परीक्षण के लिए समान नामों से बचना चाहिए।

#include "foo.h" 

static int square_test(int x) { return x + 1; } 

TEST_CLASS(ArgumentTests) 
{ 
public: 
    TEST_METHOD_CLEANUP(TearDown) 
    { 
     ISOLATOR_CLEANUP(); 
    } 

    TEST_METHOD(TestStaticReplacedStatic) 
    { 
     PRIVATE_WHEN_CALLED(NULL, square).DoStaticOrGlobalInstead(square_test, NULL); 
     Assert::IsTrue(myfunction(0, 0) == 2); 
    } 
}; 

आशा है कि यह आपके लिए उपयोगी होगा, शुभकामनाएँ!

4

मैंने Mimick लिखा, सी कार्यों के लिए एक मॉकिंग/स्टबिंग लाइब्रेरी जो इसे संबोधित करती है।

मानते हैं कि वर्ग स्थिर नहीं है और न ही इनलाइन है (क्योंकि अन्यथा यह संकलन इकाई और उसके द्वारा उपयोग किए जाने वाले कार्यों के लिए बाध्य हो जाता है) और यह कि आपके कार्यों को "libfoo.so" नामक साझा लाइब्रेरी के अंदर संकलित किया गया है (या जो कुछ भी मंच के नामकरण परंपरा है), इस आप क्या करेंगे है:

#include <stdlib.h> 
#include <assert.h> 
#include <mimick.h> 

/* Define the blueprint of a mock identified by `square_mock` 
    that returns an `int` and takes a `int` parameter. */ 
mmk_mock_define (square_mock, int, int); 

static int add_one(int x) { return x + 1; } 

int main(void) { 
    /* Mock the square function in the foo library using 
     the `square_mock` blueprint. */ 
    mmk_mock("[email protected]:foo", square_mock); 

    /* Tell the mock to return x + 1 whatever the given parameter is. */ 
    mmk_when(square(mmk_any(int)), .then_call = (mmk_fn) add_one); 

    /* Alternatively, tell the mock to return 1 if called with 0. */ 
    mmk_when(square(0), .then_return = &(int) { 1 }); 

    assert(myfunction(0, 0) == 2); 

    mmk_reset(square); 
} 

यह हालांकि एक पूर्ण विकसित मजाक समाधान है, और अगर आप केवल square (ठूंठ करना चाहते हैं और परीक्षण बातचीत के बारे में परवाह नहीं है), तो आप कर सकते थे कुछ ऐसा करें:

#include <stdlib.h> 
#include <assert.h> 
#include <mimick.h> 

static int my_square(int x) { return x + 1; } 

int main(void) { 
    mmk_stub("[email protected]:foo", my_square); 

    assert(myfunction(0, 0) == 2); 

    mmk_reset(square); 
} 

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

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