यदि एप्लिकेशन को शुद्ध स्विफ्ट परियोजना में यूनिट परीक्षण चल रहा है तो उसे कैसे जाने दें?


87

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

प्रीप्रोसेसर मैक्रोज़ चले जाने के साथ, मेरी परियोजना के लिए सबसे अच्छा क्या है कि वह इस बात से अवगत रहे कि यह परीक्षण चल रहा है और एक सामान्य लॉन्च नहीं है? मैं उन्हें सामान्य रूप से command+ Uऔर एक बॉट पर चलाता हूं ।

स्यूडोकोड:

// Appdelegate.swift
if runningTests() {
   return
} else {
   // do ordinary api calls
}

"पूरे ऐप को अपना स्टोरीबोर्ड चलाना और लॉन्च करना है और रूट व्यू कंट्रोलर" क्या यह सही है? मैंने इसका परीक्षण नहीं किया है लेकिन यह मुझे सही नहीं लगता है। हम्म ...
फॉगमिस्टर

हाँ, आवेदन किया था खत्म शुरूआत अच्छी तरह से रूट दृश्य नियंत्रक के लिए viewDidLoad के रूप में के रूप में चलाया जाता है
Bogen

आह, बस परीक्षण किया गया। नहीं सोचा था कि मामला लोल था। इसके बारे में ऐसा क्या है जो आपके परीक्षणों में समस्या पैदा कर रहा है? हो सकता है कि इसके आसपास कोई और तरीका हो?
फोगमिस्टर

मुझे बस ऐप को उसके टेस्ट को ध्यान में रखते हुए चलाने की आवश्यकता है, इसलिए पुराने प्रीप्रोसेसर मैक्रोज़ की तरह एक झंडा काम करेगा, लेकिन वे तेजी से समर्थित नहीं हैं।
बोजन

हाँ, लेकिन ऐसा करने के लिए आपको "आवश्यकता" क्यों है? यह क्या है जो आपको लगता है कि आपको ऐसा करने की आवश्यकता है?
फोगमिस्टर

जवाबों:


44

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

यहाँ छवि विवरण दर्ज करें


1
मेजबान एप्लिकेशन को हटाने के बाद लक्ष्य के लिए ब्रिजिंग हेडर कैसे शामिल करें?
भार्गव

1
यह कोशिश की, यह मेरे परीक्षणों को तोड़ दिया जब तक कि मुझे यह जवाब नहीं मिला कि बस विपरीत करने का प्रस्ताव है: stackoverflow.com/a/30939244/1602270
eagle.dan.1349

अगर मैं ऐसा करता हूं, तो मैं CloudKit का परीक्षण कर सकता हूं (उदाहरण के लिए) इसलिए मेरे लिए समाधान है कि मैं ApplicationDidFinishLaunching में पता लगाऊं कि क्या मैं परीक्षण कर रहा हूं, और यदि हां, तो एप्लिकेशन के मुख्य वर्गों को आवंटित किए बिना वापस आ जाएं।
user1105951

86

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

if NSProcessInfo.processInfo().environment["XCTestConfigurationFilePath"] != nil {
     // Code only executes when tests are running
}

मैंने इस उत्तर में वर्णित एक सशर्त संकलन ध्वज का उपयोग किया था ताकि रनटाइम लागत केवल डिबग बिल्ड में खर्च हो:

#if DEBUG
    if NSProcessInfo.processInfo().environment["XCTestConfigurationFilePath"] != nil {
        // Code only executes when tests are running
    }
#endif

स्विफ्ट 3.0 को संपादित करें

if ProcessInfo.processInfo.environment["XCTestConfigurationFilePath"] != nil {
    // Code only executes when tests are running
}

3
नवीनतम Xcode में काम नहीं करता है - पर्यावरण चर का नाम बदल गया है
amleszk

7
निश्चित रूप से ठीक नहीं जब यह काम करना बंद कर देता है, लेकिन Xcode 7.3 के साथ मैं अब XCTestConfigurationFilePathपर्यावरण कुंजी का उपयोग कर रहा हूं XCInjectBundle
ऑस्प

धन्यवाद @ospr, मैंने Xcode 7.3 के साथ काम करने का जवाब संपादित किया है
माइकल

@tkuichooseyou के po ProcessInfo.processInfo.environmentपास कोई कुंजी नहीं है XCTestConfigurationFilePath। क्या आप कृपया अपना कोड साझा कर सकते हैं? यह फिर से एक UITest लक्ष्य
ताल Zion

@ तालियन क्षमा करें, आपको एहसास नहीं था कि आप एक सबसे बड़े लक्ष्य के लिए हैं। क्या आपने NSClassFromString("XCTest")नीचे दी गई विधि को आजमाया?
तुकुचोसेयौ

44

मैं अनुप्रयोग में इसका उपयोग करता हूं: didFishishLaunchingWithOptions:

// Return if this is a unit test
if let _ = NSClassFromString("XCTest") {
    return true
}

3
यह केवल यूनिट टेस्ट पर लागू होता है न कि नए Xcode 7 UI टेस्ट पर।
जेसी

34

अन्य, मेरी राय में सरल तरीके से:

आप अपने ऐप में लॉन्च तर्क के रूप में बूलियन मान को पारित करने के लिए अपनी योजना को संपादित करते हैं। ऐशे ही:

Xcode में लॉन्च तर्क सेट करें

सभी लॉन्च तर्क अपने आप जुड़ जाते हैं NSUserDefaults

आप अब BOOL प्राप्त कर सकते हैं जैसे:

BOOL test = [[NSUserDefaults standardUserDefaults] boolForKey:@"isTest"];

2
ऐसा लगता है कि यह सबसे साफ तरीका है जो मैंने अब तक पाया है। हम परीक्षण लक्षित करने के लिए सभी एप्लिकेशन फ़ाइलें जोड़ने के लिए की जरूरत नहीं है और हम कुछ अजीब समाधान के लिए जाँच पर भरोसा करने की जरूरत नहीं है "XCTestConfigurationFilePath"या NSClassFromString("XCTest")। मैंने स्विफ्ट में इस समाधान को एक वैश्विक समारोह के साथ लागू किया हैfunc isRunningTests() -> Bool { return UserDefaults.standard.bool(forKey: "isRunningTests") }
केविन हिर्श

21

मेरा मानना ​​है कि यह जानना पूरी तरह से वैध है कि आप परीक्षण के अंदर चल रहे हैं या नहीं। ऐसे कई कारण हैं जिनकी वजह से यह मददगार हो सकता है। उदाहरण के लिए, रनिंग टेस्ट में, मैं एप्लिकेशन डेलीगेट में एप्लिकेशन-किया / विल-फिनिश-लॉन्चिंग विधियों से जल्दी लौटता हूं, जिससे परीक्षण तेजी से शुरू होता है कोड के लिए नहीं मेरी यूनिट परीक्षण के लिए। फिर भी, मैं अन्य कारणों की मेजबानी के लिए शुद्ध "तर्क" परीक्षण नहीं कर सकता।

मैं @Michael McGuire द्वारा वर्णित उत्कृष्ट तकनीक का उपयोग करता था। हालाँकि, मैंने देखा कि Xcode 6.4 / iOS8.4.1 के आसपास मेरे लिए काम करना बंद कर दिया (शायद यह जल्द ही टूट गया)।

अर्थात्, मैं XCInjectBundle को किसी भी समय परीक्षण के अंदर परीक्षण के दौरान खदान के ढांचे के लिए नहीं देखता। यही है, मैं एक परीक्षण लक्ष्य के अंदर चल रहा हूं जो एक रूपरेखा का परीक्षण करता है।

इसलिए, @Fogmeister दृष्टिकोण का उपयोग करते हुए, मेरी प्रत्येक परीक्षण योजना अब एक पर्यावरण चर निर्धारित करती है जिसे मैं जांच सकता हूं।

यहाँ छवि विवरण दर्ज करें

फिर, यहाँ मेरे पास एक कोड है जिसे कक्षा में बुलाया जाता है APPSTargetConfigurationजो मेरे लिए इस सरल प्रश्न का उत्तर दे सकता है।

static NSNumber *__isRunningTests;

+ (BOOL)isRunningTests;
{
    if (!__isRunningTests) {
        NSDictionary *environment = [[NSProcessInfo processInfo] environment];
        NSString *isRunningTestsValue = environment[@"APPS_IS_RUNNING_TEST"];
        __isRunningTests = @([isRunningTestsValue isEqualToString:@"YES"]);
    }

    return [__isRunningTests boolValue];
}

इस दृष्टिकोण के साथ एक चेतावनी यह है कि यदि आप अपनी मुख्य ऐप योजना से एक परीक्षण चलाते हैं, जैसा कि XCTest आपको करने देगा, (अर्थात, आपकी एक परीक्षण योजना का चयन नहीं करना), तो आपको यह वातावरण चर सेट नहीं मिलेगा।


5
इसे 'रन' में जोड़ने के बजाय, यदि हम इसे सभी योजनाओं के लिए 'टेस्ट' में जोड़ दें तो यह अधिक सहायक नहीं होगा? इस तरह, isRunningTests सभी योजनाओं में काम करेगा।
विशाल सिंह

@VishalSingh हां, मेरा मानना ​​है कि क्लीनर है। क्या आपने वह तरीका आजमाया है? हमें बताएं कि क्या यह आपके लिए भी काम करता है।
idStar

16
var isRunningTests: Bool {
    return ProcessInfo.processInfo.environment["XCTestConfigurationFilePath"] != nil
}

प्रयोग

if isRunningTests {
    return "lena.bmp"
}
return "facebook_profile_photo.bmp"

कुछ अन्य उत्तरों के विपरीत, यह काम करता है। धन्यवाद।
n13

8

@ जेसी और @ मिकेल मैकगायर का संयुक्त दृष्टिकोण

(जैसा कि स्वीकृत उत्तर ढांचा विकसित करते समय आपकी मदद नहीं करेगा)

तो यहाँ कोड है:

#if DEBUG
        if (NSClassFromString(@"XCTest") == nil) {
            // Your code that shouldn't run under tests
        }
#else
        // unconditional Release version
#endif

1
यह बहुत अच्छा है! छोटे और रूपरेखा संगत!
blackjacx

स्विफ्ट पैकेज पर काम कर रहा था और इसने मेरे लिए काम किया
डी। ग्रेग

4

यहां एक तरीका है जो हम स्विफ्ट 4 / एक्सकोड 9 में हमारे यूनिट परीक्षणों के लिए उपयोग कर रहे हैं। यह जेसी के जवाब पर आधारित है ।

स्टोरीबोर्ड को पूरी तरह से लोड होने से रोकना आसान नहीं है, लेकिन यदि आप इसे डिफाइंडली लैंचिंग की शुरुआत में जोड़ते हैं तो यह आपके डेवलपर्स के लिए बहुत स्पष्ट हो जाता है कि क्या चल रहा है:

func application(_ application: UIApplication,
                 didFinishLaunchingWithOptions launchOptions:
                 [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    #if DEBUG
    if let _ = NSClassFromString("XCTest") {
        // If we're running tests, don't launch the main storyboard as
        // it's confusing if that is running fetching content whilst the
        // tests are also doing so.
        let viewController = UIViewController()
        let label = UILabel()
        label.text = "Running tests..."
        label.frame = viewController.view.frame
        label.textAlignment = .center
        label.textColor = .white
        viewController.view.addSubview(label)
        self.window!.rootViewController = viewController
        return true
    }
    #endif

(आप स्पष्ट रूप से यूआई परीक्षणों के लिए ऐसा कुछ नहीं करना चाहिए जहां आप एप्लिकेशन को सामान्य रूप से स्टार्टअप करना चाहते हैं!)


3

आप यहां योजना के आधार पर ऐप में रनटाइम तर्क पास कर सकते हैं ...

यहाँ छवि विवरण दर्ज करें

लेकिन मैं सवाल करूंगा कि वास्तव में इसकी जरूरत है या नहीं।


3

यह इसे करने का स्विफ्ट तरीका है।

extension Thread {
  var isRunningXCTest: Bool {
    for key in self.threadDictionary.allKeys {
      guard let keyAsString = key as? String else {
        continue
      }

      if keyAsString.split(separator: ".").contains("xctest") {
        return true
      }
    }
    return false
  }
}

और यह आप इसे कैसे उपयोग करते हैं:

if Thread.current.isRunningXCTest {
  // test code goes here
} else {
  // other code goes here
}

पूरा लेख यहां है: https://medium.com/@theinkedengineer/check-if-app-is-running-unit-tests-the-swift-way-b51fbfd07989


कि लूप के लिए इस तरह स्विफ्ट की जा सकती है:return threadDictionary.allKeys.anyMatch { ($0 as? String)?.split(separator: ".").contains("xctest") == true }
रोजर ओबा

2

इनमें से कुछ दृष्टिकोण यूआईटी के साथ काम नहीं करते हैं और यदि आप मूल रूप से ऐप कोड के साथ ही परीक्षण कर रहे हैं (यूआईटी लक्ष्य में विशिष्ट कोड जोड़ने के बजाय)।

मैंने परीक्षण के सेटअप विधि में एक पर्यावरण चर स्थापित करना समाप्त किया:

XCUIApplication *testApp = [[XCUIApplication alloc] init];

// set launch environment variables
NSDictionary *customEnv = [[NSMutableDictionary alloc] init];
[customEnv setValue:@"YES" forKey:@"APPS_IS_RUNNING_TEST"];
testApp.launchEnvironment = customEnv;
[testApp launch];

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

तब मेरे ऐप कोड में, मैं इस पर्यावरण चर की तलाश करता हूं कि क्या / जब मैं एक परीक्षण के दौरान कुछ कार्यक्षमता को बाहर करना चाहता हूं:

BOOL testing = false;
...
if (! testing) {
    NSDictionary *environment = [[NSProcessInfo processInfo] environment];
    NSString *isRunningTestsValue = environment[@"APPS_IS_RUNNING_TEST"];
    testing = [isRunningTestsValue isEqualToString:@"YES"];
}

नोट - ऋषि की टिप्पणी के लिए धन्यवाद जिसने मुझे यह विचार दिया; मैंने सिर्फ एक उदाहरण के लिए इसका विस्तार किया।


2

मैं जिस विधि का उपयोग कर रहा था, उसे Xcode 12 बीटा 1 में काम करना बंद कर दिया गया था। इस प्रश्न के सभी बिल्ड आधारित उत्तरों की कोशिश करने के बाद, मैं @ ODB के उत्तर से प्रेरित था। यहाँ एक काफी सरल समाधान का एक स्विफ्ट संस्करण है जो रियल डिवाइसेस और सिमुलेटर दोनों के लिए काम करता है। यह भी काफी "रिलीज प्रूफ" होना चाहिए।

टेस्ट सेटअप में डालें:

let app = XCUIApplication()
app.launchEnvironment.updateValue("YES", forKey: "UITesting")
app.launch()

ऐप में डालें:

let isTesting: Bool = (ProcessInfo.processInfo.environment["UITesting"] == "YES")

इसके प्रयेाग के लिए:

    if isTesting {
        // Only if testing
    } else {
        // Only if not testing
    }

0

मेरे लिए काम किया:

उद्देश्य सी

[[NSProcessInfo processInfo].environment[@"DYLD_INSERT_LIBRARIES"] containsString:@"libXCTTargetBootstrapInject"]

स्विफ्ट: ProcessInfo.processInfo.environment["DYLD_INSERT_LIBRARIES"]?.contains("libXCTTargetBootstrapInject") ?? false


0

पहले परीक्षण के लिए चर जोड़ें:

यहाँ छवि विवरण दर्ज करें

और अपने कोड में इसका उपयोग करें:

 if ProcessInfo.processInfo.environment["IS_UNIT_TESTING"] == "1" {
                 // Code only executes when tests are running
 } 

0

यदि आप नए का उपयोग कर रहे हैं XCTestBundlePath, XCTestConfigurationFilePathतो इसके बजाय Xcode12 में स्पष्ट रूप से हमें पर्यावरण कुंजी में खोजना होगाXCTestPlan

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.