देरी / एक्सकोड यूआई परीक्षण के एक परीक्षण के मामले में प्रतीक्षा करें


182

मैं Xcode 7 बीटा 2 में उपलब्ध नए UI परीक्षण का उपयोग करके एक परीक्षण केस लिखने की कोशिश कर रहा हूं। ऐप में एक लॉगिन स्क्रीन है जहां यह सर्वर पर लॉगिन करने के लिए कॉल करता है। इससे जुड़ा एक विलंब है क्योंकि यह एक अतुल्यकालिक ऑपरेशन है।

क्या आगे कदम बढ़ाने से पहले XCTestCase में देरी या प्रतीक्षा तंत्र का कारण है?

कोई उचित दस्तावेज उपलब्ध नहीं है और मैं कक्षाओं की हैडर फाइलों के माध्यम से चला गया। इससे संबंधित कुछ भी खोजने में सक्षम नहीं था।

कोई विचार / सुझाव?


13
मुझे लगता है कि NSThread.sleepForTimeInterval(1)काम करना चाहिए
Kametrixom

महान! ऐसा लगता है कि यह काम करता है। लेकिन मुझे यकीन नहीं है कि यह करने का अनुशंसित तरीका है। मुझे लगता है कि एप्पल को इसे करने का बेहतर तरीका देना चाहिए। राडार
तेजस एचएस

मुझे वास्तव में लगता है कि यह ठीक है, यह वास्तव में एक निश्चित समय के लिए वर्तमान थ्रेड को रोकने का सबसे आम तरीका है। यदि आप अधिक नियंत्रण चाहते हैं तो आप GCD (द dispatch_after, dispatch_queueसामान)
Kametrixom

@Kametrixom रन लूप को टिक न करें - Apple ने बीटा में देशी एसिंक्रोनस परीक्षण पेश किया । विवरण के लिए मेरा उत्तर देखें।
जो मासीलोटी

2
स्विफ्ट 4.0 -> थ्रेड.स्लीप (forTimeInterval: 2)
uplearnedu.com

जवाबों:


168

अतुल्यकालिक यूआई परीक्षण Xcode 7 बीटा 4 में पेश किया गया था। पाठ के साथ एक लेबल के लिए प्रतीक्षा करने के लिए "हैलो, दुनिया!" आप निम्नलिखित कर सकते हैं:

let app = XCUIApplication()
app.launch()

let label = app.staticTexts["Hello, world!"]
let exists = NSPredicate(format: "exists == 1")

expectationForPredicate(exists, evaluatedWithObject: label, handler: nil)
waitForExpectationsWithTimeout(5, handler: nil)

UI परीक्षण के बारे में अधिक जानकारी मेरे ब्लॉग पर पाई जा सकती है।


19
दुर्भाग्य से यह स्वीकार करने का कोई तरीका नहीं है कि टाइमआउट हुआ और आगे बढ़े - waitForExpectationsWithTimeoutस्वचालित रूप से आपके परीक्षण को विफल कर देगा जो काफी दुर्भाग्यपूर्ण है।
जेडीजा

@ जेडीजा दरअसल, मेरे लिए एक्सकोड 7.0.1 के साथ ऐसा नहीं होता है।
बैस्टियन

@ बास्तियन हम्म दिलचस्प; मुझे इस पर फिर से विचार करना होगा।
जेडीडजा

1
यह मेरे लिए काम नहीं करता है। यहाँ मेरा नमूना है: xButton = app.toolbars.buttons ["X"] को मौजूद रहने दें = NSPredicate (स्वरूप: "मौजूद == 1") प्रत्याशाForPredicate (मौजूद, मूल्यांकन किया गया Withbbject: xButton, हैंडलर: nil) waForExpectationsWithTimeout (10) nil)
इमोलुमासी

app.launch()बस एप्लिकेशन को पुन: लॉन्च करने लगता है। क्या ये ज़रूरी हैं?
क्रिस प्रिंस

224

इसके अतिरिक्त, आप बस सो सकते हैं:

sleep(10)

चूंकि UITests एक अन्य प्रक्रिया में चलती है, इसलिए यह काम करता है। मुझे नहीं पता कि यह कितना उचित है, लेकिन यह काम करता है।


2
कुछ समय हमें देरी करने के तरीके की आवश्यकता होती है और यह असफलता नहीं चाहता है! धन्यवाद
ताई ले

13
सबसे अच्छा जवाब जो मैंने कभी देखा है :) मैं + 100 वोट
जोड़ूंगा

8
मुझे NSThread.sleepForTimeInterval (0.2) पसंद है क्योंकि आप उप-द्वितीय देरी निर्दिष्ट कर सकते हैं। (नींद) एक पूर्णांक पैरामीटर लेता है, केवल एक सेकंड के गुणक संभव हैं)।
ग्राहम पर्क्स

5
@GrahamPerks, हाँ, हालाँकि वहाँ भी है:usleep
mxcl

3
यह एक खराब सुझाव नहीं है (आपको यह पता नहीं है कि UITesting कैसे काम करता है), लेकिन भले ही यह एक खराब सुझाव था कभी-कभी एक उम्मीद को तैयार करने का कोई तरीका नहीं है कि काम करता है (सिस्टम अलर्ट किसी को भी?) तो यह सब आपके पास है।
mxcl

78

iOS 11 / Xcode 9

<#yourElement#>.waitForExistence(timeout: 5)

यह इस साइट पर सभी कस्टम कार्यान्वयन के लिए एक महान प्रतिस्थापन है!

: मेरा उत्तर यहाँ पर एक नजर है के लिए सुनिश्चित करें https://stackoverflow.com/a/48937714/971329 । वहाँ मैं अनुरोधों के लिए प्रतीक्षा करने के लिए एक विकल्प का वर्णन करता हूं जो आपके परीक्षणों के चलने के समय को बहुत कम कर देगा!


धन्यवाद @daidai मैं पाठ :) बदल
blackjacx

1
हां, यह अभी भी दृष्टिकोण है जिसका उपयोग करते समय मैं जा रहा हूं XCTestCaseऔर यह एक आकर्षण की तरह काम करता है। मुझे समझ में नहीं आ रहा है कि अप्रोच sleep(3)को इतने ऊंचे स्तर पर क्यों वोट दिया जाता है क्योंकि यह परीक्षण के समय को कृत्रिम रूप से बढ़ाता है और वास्तव में कोई विकल्प नहीं है जब आपका टेस्ट सूट बढ़ता है।
ब्लेक जेक

वास्तव में इसके लिए Xcode 9 की आवश्यकता होती है, लेकिन iOS 10 चलाने वाले उपकरणों / सिमुलेटरों पर भी काम करता है ;-)
d4Rk

हाँ मैंने लिखा है कि ऊपर शीर्षक पर। लेकिन अब ज्यादातर लोगों को कम से कम Xcode 9 ;-) में अपग्रेड किया जाना चाहिए था
Blackjacx

77

Xcode 9 ने XCTWaiter के साथ नई चालें पेश कीं

टेस्ट केस स्पष्ट रूप से प्रतीक्षा करता है

wait(for: [documentExpectation], timeout: 10)

वेटर उदाहरण परीक्षण करने के लिए प्रतिनिधियों

XCTWaiter(delegate: self).wait(for: [documentExpectation], timeout: 10)

वेटर क्लास का रिजल्ट

let result = XCTWaiter.wait(for: [documentExpectation], timeout: 10)
switch(result) {
case .completed:
    //all expectations were fulfilled before timeout!
case .timedOut:
    //timed out before all of its expectations were fulfilled
case .incorrectOrder:
    //expectations were not fulfilled in the required order
case .invertedFulfillment:
    //an inverted expectation was fulfilled
case .interrupted:
    //waiter was interrupted before completed or timedOut
}

नमूना उपयोग

Xcode 9 से पहले

उद्देश्य सी

- (void)waitForElementToAppear:(XCUIElement *)element withTimeout:(NSTimeInterval)timeout
{
    NSUInteger line = __LINE__;
    NSString *file = [NSString stringWithUTF8String:__FILE__];
    NSPredicate *existsPredicate = [NSPredicate predicateWithFormat:@"exists == true"];

    [self expectationForPredicate:existsPredicate evaluatedWithObject:element handler:nil];

    [self waitForExpectationsWithTimeout:timeout handler:^(NSError * _Nullable error) {
        if (error != nil) {
            NSString *message = [NSString stringWithFormat:@"Failed to find %@ after %f seconds",element,timeout];
            [self recordFailureWithDescription:message inFile:file atLine:line expected:YES];
        }
    }];
}

उपयोग

XCUIElement *element = app.staticTexts["Name of your element"];
[self waitForElementToAppear:element withTimeout:5];

तीव्र

func waitForElementToAppear(element: XCUIElement, timeout: NSTimeInterval = 5,  file: String = #file, line: UInt = #line) {
        let existsPredicate = NSPredicate(format: "exists == true")

        expectationForPredicate(existsPredicate,
                evaluatedWithObject: element, handler: nil)

        waitForExpectationsWithTimeout(timeout) { (error) -> Void in
            if (error != nil) {
                let message = "Failed to find \(element) after \(timeout) seconds."
                self.recordFailureWithDescription(message, inFile: file, atLine: line, expected: true)
            }
        }
    }

उपयोग

let element = app.staticTexts["Name of your element"]
self.waitForElementToAppear(element)

या

let element = app.staticTexts["Name of your element"]
self.waitForElementToAppear(element, timeout: 10)

स्रोत


1
ऊपर दिए गए xcode9 उदाहरण के बारे में कुछ और दृष्टांतों की तलाश
rd_


1
का परीक्षण किया। एक जादू की तरह काम करता है! धन्यवाद!
दाविद कोनसीविज़

32

8.3 Xcode के रूप में, हम http://masilotti.com/xctest-waiting/ का उपयोग कर सकते हैंXCTWaiter

func waitForElementToAppear(_ element: XCUIElement) -> Bool {
    let predicate = NSPredicate(format: "exists == true")
    let expectation = expectation(for: predicate, evaluatedWith: element, 
                                  handler: nil)

    let result = XCTWaiter().wait(for: [expectation], timeout: 5)
    return result == .completed
}

एक और ट्रिक है एक waitफंक्शन को लिखने का , इसका श्रेय जॉन सुंडेल को जाता है कि यह मुझे दिखाया जाए

extension XCTestCase {

  func wait(for duration: TimeInterval) {
    let waitExpectation = expectation(description: "Waiting")

    let when = DispatchTime.now() + duration
    DispatchQueue.main.asyncAfter(deadline: when) {
      waitExpectation.fulfill()
    }

    // We use a buffer here to avoid flakiness with Timer on CI
    waitForExpectations(timeout: duration + 0.5)
  }
}

और इसका उपयोग करें

func testOpenLink() {
  let delegate = UIApplication.shared.delegate as! AppDelegate
  let route = RouteMock()
  UIApplication.shared.open(linkUrl, options: [:], completionHandler: nil)

  wait(for: 1)

  XCTAssertNotNil(route.location)
}

11

@ टेड के उत्तर के आधार पर , मैंने इस एक्सटेंशन का उपयोग किया है:

extension XCTestCase {

    // Based on https://stackoverflow.com/a/33855219
    func waitFor<T>(object: T, timeout: TimeInterval = 5, file: String = #file, line: UInt = #line, expectationPredicate: @escaping (T) -> Bool) {
        let predicate = NSPredicate { obj, _ in
            expectationPredicate(obj as! T)
        }
        expectation(for: predicate, evaluatedWith: object, handler: nil)

        waitForExpectations(timeout: timeout) { error in
            if (error != nil) {
                let message = "Failed to fulful expectation block for \(object) after \(timeout) seconds."
                self.recordFailure(withDescription: message, inFile: file, atLine: line, expected: true)
            }
        }
    }

}

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

let element = app.staticTexts["Name of your element"]
waitFor(object: element) { $0.exists }

यह किसी तत्व के गायब होने की प्रतीक्षा करने, या किसी अन्य संपत्ति को बदलने की अनुमति देता है (उपयुक्त ब्लॉक का उपयोग करके)

waitFor(object: element) { !$0.exists } // Wait for it to disappear

+1 बहुत ज़ोर से मारना, और यह ब्लॉक विधेय का उपयोग करता है जो मुझे लगता है कि बहुत बेहतर है क्योंकि मानक विधेय अभिव्यक्तियाँ मेरे लिए कभी-कभी काम नहीं करती थीं, उदाहरण के लिए जब XCUIElements पर कुछ संपत्तियों का इंतजार किया जाता है
लॉनिको

10

संपादित करें:

यह वास्तव में अभी मेरे पास आया है कि Xcode 7b4 में, UI परीक्षण अब है expectationForPredicate:evaluatedWithObject:handler:

मूल:

एक अन्य तरीका रन लूप को समय की एक निर्धारित राशि के लिए स्पिन करना है। वास्तव में केवल उपयोगी है यदि आप जानते हैं कि कितना (अनुमानित) समय आपको इंतजार करना होगा

Obj सी: [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow: <<time to wait in seconds>>]]

स्विफ्ट: NSRunLoop.currentRunLoop().runMode(NSDefaultRunLoopMode, beforeDate: NSDate(timeIntervalSinceNow: <<time to wait in seconds>>))

यह सुपर उपयोगी नहीं है यदि आपको अपना परीक्षण जारी रखने के लिए कुछ शर्तों का परीक्षण करने की आवश्यकता है। सशर्त जांच चलाने के लिए, एक whileलूप का उपयोग करें ।


यह मेरे लिए विशेष रूप से उदाहरण के लिए स्वच्छ और बहुत उपयोगी है, ऐप लॉन्च की प्रतीक्षा कर रहा है, प्रीलोड किए गए डेटा का अनुरोध करें और लॉगिन / लॉगआउट सामान करें। धन्यवाद।
felixwcf

4

निम्नलिखित कोड सिर्फ उद्देश्य सी के साथ काम करता है।

- (void)wait:(NSUInteger)interval {

    XCTestExpectation *expectation = [self expectationWithDescription:@"wait"];
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(interval * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        [expectation fulfill];
    });
    [self waitForExpectationsWithTimeout:interval handler:nil];
}

बस नीचे दिए गए अनुसार इस फ़ंक्शन को कॉल करें।

[self wait: 10];

त्रुटि -> "NSInternalInconsistencyException" को पकड़ा गया, "API उल्लंघन - बिना किसी सेटिंग के प्रतीक्षा करने के लिए किया गया कॉल।"
फ्लोयू। SimpleUITesting.com

@ iOSCalendarpatchthecode.com, क्या आपने इसके लिए कोई वैकल्पिक हल खोजा है?
मैक्स

@ क्या आप इस पृष्ठ पर किसी अन्य का उपयोग कर सकते हैं?
फ्लोयू। SimpleUITesting.com

@ iOSCalendarpatchthecode.com नहीं, मुझे जांचने के लिए किसी भी तत्व के बिना बस कुछ देरी की आवश्यकता है। इसलिए मुझे इसके वैकल्पिक रूप की आवश्यकता है।
मैक्स

@ मोम ने इस पृष्ठ पर चयनित उत्तर का उपयोग किया। इसने मेरे लिए काम किया। शायद आप उनसे पूछ सकते हैं कि आप विशेष रूप से क्या देख रहे हैं।
फ्लोयू। SimpleUITesting.com

4

मेरे मामले में sleepसाइड इफ़ेक्ट बनाया तो मैंने इस्तेमाल कियाwait

let _ = XCTWaiter.wait(for: [XCTestExpectation(description: "Hello World!")], timeout: 2.0)

0

एपीआई के अनुसार XCUIElement .existsका उपयोग यह जांचने के लिए किया जा सकता है कि क्या कोई क्वेरी मौजूद है या नहीं, इसलिए निम्न सिंटैक्स कुछ मामलों में उपयोगी हो सकता है!

let app = XCUIApplication()
app.launch()

let label = app.staticTexts["Hello, world!"]
while !label.exists {
    sleep(1)
}

यदि आप आश्वस्त हैं कि आपकी अपेक्षा पूरी हो जाएगी तो आप इसे चलाने का प्रयास कर सकते हैं। यह ध्यान दिया जाना चाहिए कि यदि दुर्घटना बहुत लंबी है, तो प्रतीक्षा करना मुश्किल हो सकता है जब waitForExpectationsWithTimeout(_,handler:_)@Joe Masilotti की पोस्ट का उपयोग किया जाना चाहिए।


0

नींद धागे को अवरुद्ध कर देगी

"थ्रेड ब्लॉक होने के दौरान कोई रन लूप प्रसंस्करण नहीं होता है।"

आप waForExistence का उपयोग कर सकते हैं

let app = XCUIApplication()
app.launch()

if let label = app.staticTexts["Hello, world!"] {
label.waitForExistence(timeout: 5)
}

0

यह थ्रेड को नींद में डालने या टाइमआउट पर एक त्रुटि फेंकने के बिना एक देरी पैदा करेगा:

let delayExpectation = XCTestExpectation()
delayExpectation.isInverted = true
wait(for: [delayExpectation], timeout: 5)

क्योंकि उम्मीद उलटी है, यह चुपचाप समय देगा।

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