2012-06-07 5 views
29

क्या यूनिट परीक्षण tearDown में dispatch_once कोड की स्थिति को रीसेट करना संभव है?यूनिट परीक्षण में dispatch_once की स्थिति को रीसेट करना संभव है, उन्हें फिर से चलाने के लिए

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

उत्तर

42

मुझे पहले ध्यान रखना चाहिए कि परीक्षण के अलावा किसी भी स्थिति में ऐसा करना अच्छा नहीं है; फिर भी, देखभाल के साथ आगे बढ़ें - अलीसॉफ्टर्स नीचे दी गई टिप्पणियों में कुछ विवरण और उदाहरण कोड प्रदान करता है। पर दिलचस्प उत्तरों को भी देखें, जिनमें कुछ महत्वपूर्ण जानकारी from the horse's mouth शामिल हैं।

dispatch_once_ttypedef डी long है। इसका झूठा मान 0 है। यदि आप उस ध्वज को 0 पर रीसेट करते हैं, dispatch_once() फिर से चलाएगा। आपकी समस्या "बस" एक स्थिर चर के मान को किसी अन्य संकलन इकाई से कैसे बदलें।

MakeWhoopie.h

#import <Foundation/Foundation.h> 

void makeWhoopie(void); 

#ifdef DEBUG 
void resetDispatchOnce(void); 
#endif 

MakeWhoopie.m

#include "MakeWhoopie.h" 

static dispatch_once_t * once_token_debug; 

void makeWhoopie(void) 
{ 

    static dispatch_once_t once_token; 
    once_token_debug = &once_token; // Store address of once_token 
             // to access it in debug function. 
    dispatch_once(&once_token, ^{ 
     NSLog(@"That's what you get, folks."); 
    }); 

    NSLog(@"Making whoopie."); 
} 

#ifdef DEBUG 
void resetDispatchOnce(void) 
{ 
    *once_token_debug = 0; 
} 
#endif 

(तुम भी once_token अप दायर करने के लिए ले जाने के कर सकते हैं: इस के लिए, मैं तुम्हें एक डिबग/इकाई परीक्षण हुक, इसलिए तरह की जरूरत है । के स्तर और इसे सीधे परिवर्तित)

इस बाहर की कोशिश कर रहा:

#import <Foundation/Foundation.h> 
#import "MakeWhoopie.h" 

int main(int argc, const char * argv[]) 
{ 

    @autoreleasepool { 

     makeWhoopie(); 
     makeWhoopie(); 
     resetDispatchOnce(); 
     makeWhoopie(); 
    } 
    return 0; 
} 
में

परिणाम:

2012-06-07 18: 45: 28.134 ResetDispatchOnce [8628: 403] यही कारण है कि लोगों को आप क्या मिलता है,।
2012-06-07 18: 45: 28.163 रीसेट डिस्पैचऑन [8628: 403] व्हाउपी बनाना।
2012-06-07 18: 45: 28.164 रीसेट डिस्पैचऑन [8628: 403] व्हाउपी बनाना।
2012-06-07 18: 45: 28.165 रीसेट डिस्पैचऑन [8628: 403] यही आपको मिलता है, दोस्तों।
2012-06-07 18: 45: 28.165 रीसेट डिस्पैचऑन [8628: 403] व्हाउपी बनाना।

+0

'dispatch_once' का बिंदु यह है कि यह थ्रेड-सुरक्षित है। इस तरह '* one_token_debug = 0' सेट करके समस्या यह है कि यह थ्रेड-सुरक्षित नहीं है क्योंकि अन्य थ्रेड 'dispatch_once (और एक बार टोकन, ...)' का उपयोग करते हैं, जबकि आप अपने द्वारा 'एक बार टोकन' सेट करते हैं। हम इस तरह के मुद्दे को कैसे रोक सकते हैं? – AliSoftware

+0

@AliSoftware: प्रश्न में परिदृश्य इकाई परीक्षण है, और यह _only_ उपयोग है जिसे मैं इस प्रक्रिया के लिए प्रस्तावित करता हूं। टोकन को _between_ परीक्षणों को रीसेट किया जा रहा है, ताकि एक साफ स्लेट बनाने के लिए जैसा कि बड़े कार्यक्रम के रनों के बीच बनाया जाएगा। थ्रेडिंग कोई मुद्दा नहीं है क्योंकि प्रोग्राम परीक्षणों के बीच नहीं चल रहा है। –

+0

मुझे यह समझा गया, लेकिन सभी को इसके बारे में पता होना चाहिए और इसे कहीं और गैर-थ्रेडसेफ तरीके से उपयोग करने के लिए लुभाना नहीं है।और यूनिट टेस्ट में भी कुछ मामले हो सकते हैं कि यह थ्रेड-सुरक्षित नहीं है, विशेष रूप से यदि कोई बुरी तरह से कोड का परीक्षण करता है और परीक्षण के समाप्त होने के बाद भी चल रहे असीमित क्रियाएं करता है (जैसे कि प्रेषण '(10s,^{/ * sthg जो साझाInstance * /} का उपयोग करता है, यदि परीक्षण अपने टाइमआउट तक पहुंच जाता है तो यह असफल हो जाएगा और बंद हो जाएगा और 'आंसू डाउन' तक पहुंच जाएगा लेकिन साझा किए गए उपयोग का उपयोग ब्लॉक अभी भी निकट भविष्य में भेजा जाएगा ... आंसू के बाद। – AliSoftware

4

हम भी यूनिट हमारे सिंगलेट का परीक्षण करते हैं और कभी-कभी उन्हें नकली वस्तुओं के साथ बदलने या उन्हें रीसेट करने की आवश्यकता होती है। मैंने जोश का जवाब लिया और इसे थोड़ा सा सरल बना दिया:

static ArticleManager *_sharedInstance = nil; 
static dispatch_once_t once_token = 0; 

+(ArticleManager *)sharedInstance { 
    dispatch_once(&once_token, ^{ 
     if (_sharedInstance == nil) { 
      _sharedInstance = [[ArticleManager alloc] init]; 
     } 
    }); 
    return _sharedInstance; 
} 

+(void)setSharedInstance:(ArticleManager *)instance { 
    if (instance == nil) once_token = 0; 
    _sharedInstance = instance; 
} 
+0

मैंने एक ब्लॉग पोस्ट लिखा है कि आपको यह क्यों करना चाहिए: http://twobitlabs.com/2013/01/objective-c-singleton-pattern-unit-testing/ – ToddH

+0

मैंने फ़ाइल स्तर पर एक बार टोकन डालने का उल्लेख किया मेरा जवाब। ध्यान दें कि मेरे - या किसी के भी - कोड को एसओ पर पोस्ट किया गया है, चाहे वर्बैटिम या संशोधित है, [आवश्यक] (http://creativecommons.org/licenses/by-sa/3.0/) जिसमें आप [एट्रिब्यूशन] (http शामिल हैं) : //blog.stackoverflow.com/2009/06/attribution-required/)। इस मामले में तंत्र इतना सरल है कि यह शायद ही उल्लेखनीय है, लेकिन कृपया कम से कम लिंक किए बिना SO उत्तरों के आधार पर ब्लॉगिंग की आदत में न आएं। –

+0

वास्तव में, dispatch_once के अंदर == शून्य जांच अनावश्यक प्रतीत होती है। _sharedInstance को सेट किया गया था, तो आप केवल एक बार टोकन को रीसेट कर दें। – Vitali