Xcode UI testinin bir test durumunda Gecikme / Bekleme


182

Xcode 7 beta 2'de mevcut yeni UI Testi kullanarak bir test senaryosu yazmaya çalışıyorum. Uygulama, giriş yapmak için sunucuya bir çağrı yapan bir giriş ekranına sahiptir. Zaman uyumsuz bir işlem olduğu için bununla ilgili bir gecikme vardır.

Sonraki adımlara geçmeden önce XCTestCase'de gecikme veya bekleme mekanizmasına neden olmanın bir yolu var mı?

Uygun bir belge yok ve sınıfların Başlık dosyalarından geçtim. Bununla ilgili hiçbir şey bulamadı.

Herhangi bir fikir / öneriniz var mı?


13
Bence NSThread.sleepForTimeInterval(1)çalışması gerekir
Kametrixom

Harika! Bu işe yarıyor gibi görünüyor. Ama bunu yapmanın önerilen yolu olup olmadığından emin değilim. Apple'ın bunu yapmak için daha iyi bir yol vermesi gerektiğini düşünüyorum. Radar
Tejas HS

Aslında bunun iyi olduğunu düşünüyorum, mevcut iş parçasını belirli bir süre duraklatmanın en yaygın yolu gerçekten. Daha fazla kontrol istiyorsanız dispatch_afterdispatch_queue
GCD'ye

@Kametrixom Do çalıştırmak döngü kene değil - Elma Bkz 4. Beta yerli asenkron test tanıtıldı cevabımı detayları için.
Joe Masilotti

2
Swift 4.0 -> Thread.sleep (forTimeInterval: 2)
uplearnedu.com

Yanıtlar:


168

Xcode 7 Beta 4'te Asenkron UI Testi tanıtıldı. "Merhaba dünya!" Metnini içeren bir etiket beklemek için. görünmek için aşağıdakileri yapabilirsiniz:

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)

Kullanıcı Arayüzü Testi hakkında daha fazla bilgiyi blogumda bulabilirsiniz.


19
Ne yazık ki zaman aşımının gerçekleştiğini ve ilerlediğini kabul etmenin bir yolu yok - waitForExpectationsWithTimeoutoldukça talihsiz olan testinizde otomatik olarak başarısız olacaktır.
Jedidja

@Jedidja Aslında, bu benim için XCode 7.0.1 ile olmaz.
Bastian

@Bastian Hmm ilginç; Bunu tekrar kontrol etmem gerekecek.
Jedidja

1
benim için işe yaramıyor. İşte benim örnek: let xButton = app.toolbars.buttons ["X"] izin ver = NSPredicate (biçim: "exist == 1") beklentiForPredicate (var, değerlendirmeWithObject: xButton, işleyici: nil) waitForExpectationsWithTimeout (10, handler: nil)
emoleumassi

app.launch()Sadece uygulamayı tekrar başlatmanız gibi görünüyor. Bu gerekli mi?
Chris Prince

225

Ayrıca, sadece uyuyabilirsiniz:

sleep(10)

UITest'ler başka bir işlemde çalıştığından, bu işe yarar. Ne kadar tavsiye edilebilir bilmiyorum, ama işe yarıyor.


2
Bir süre erteleme yoluna ihtiyacımız var ve bir başarısızlık yaratmasını istemiyoruz! teşekkürler
Tai Le

13
Şimdiye kadar gördüğüm en iyi cevap :) Yapabilirsem + 100 oy eklerim :)
Bartłomiej Semańczyk

8
NSThread.sleepForTimeInterval (0.2) gibi ikinci saniye gecikmeleri belirtebilirsiniz. (sleep () bir tamsayı parametresi alır; yalnızca saniyenin katları mümkündür).
Graham Perks

5
@GrahamPerks, evet, yine de var:usleep
mxcl

3
Kötü bir öneri değildir (UITesting'in nasıl çalıştığını anlamıyorsunuz), ancak kötü bir öneri olsa bile bazen çalışan bir beklenti oluşturmanın bir yolu yoktur (sistem herkesi uyarır?) Bu yüzden sahip olduğunuz tek şey budur.
mxcl

78

iOS 11 / Xcode 9

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

Bu, bu sitedeki tüm özel uygulamalar için harika bir alternatif!

Cevabımı burada gördüğünüzden emin olun: https://stackoverflow.com/a/48937714/971329 . Orada, testlerinizin çalışma süresini büyük ölçüde azaltacak talepleri beklemeye bir alternatif açıklıyorum!


Teşekkürler @daidai Metni değiştirdim :)
blackjacx

1
Evet, bu hala kullanacağım yaklaşım XCTestCaseve bir cazibe gibi çalışıyor. sleep(3)Test sürelerini yapay olarak uzattığı ve test takımınız büyüdüğünde gerçekten bir seçenek olmadığından, bu tür yaklaşımların neden bu kadar yüksek seçildiğini anlamıyorum .
blackjacx

Aslında Xcode 9 gerektirir, ancak iOS 10 çalıştıran cihazlarda / simülatörlerde de çalışır ;-)
d4Rk 13:18

Evet, bunu yukarıdaki başlığa yazdım. Ama şimdi çoğu insan en az Xcode 9 ;-) 'e yükseltilmiş olmalı
blackjacx

77

Xcode 9 , XCTWaiter ile yeni numaralar getirdi

Test davası açık bir şekilde bekliyor

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

Garson örneği test etmek için temsilci seçiyor

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

Garson sınıfı sonucu döndürüyor

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
}

örnek kullanım

Xcode 9'dan önce

Hedef C

- (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];
        }
    }];
}

KULLANIM

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

hızlı

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)
            }
        }
    }

KULLANIM

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

veya

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

KAYNAK


1
Yukarıdaki xcode9 örneği ile ilgili daha fazla örnek arıyorum
rd_


1
Test edilmiştir. Tıkır tıkır çalışıyor! Teşekkürler!
Dawid Koncewicz

32

Xcode 8.3 itibariyle http://masilotti.com/xctest-waiting/ adresini kullanabilirizXCTWaiter

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
}

Başka bir hile, bir waitişlev yazmak , kredi bana göstermek için John Sundell'e gidiyor

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)
  }
}

ve onu gibi kullan

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

@ Ted'in cevabına dayanarak şu uzantıyı kullandım:

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)
            }
        }
    }

}

Bu şekilde kullanabilirsiniz

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

Ayrıca bir öğenin kaybolmasını veya başka bir özelliğin değişmesini bekler (uygun bloğu kullanarak).

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

+1 çok swifty ve çok daha iyi olduğunu düşündüğüm blok yüklemini kullanıyor çünkü standart yüklem ifadeleri bazen benim için işe yaramadı, örneğin XCUIElements vb. Bazı özellikleri beklerken
lawicko

10

Düzenle:

Aslında Xcode 7b4'te UI testinin şimdi expectationForPredicate:evaluatedWithObject:handler:

Orijinal:

Başka bir yol, çalışma döngüsünü belirli bir süre döndürmektir. Gerçekten sadece ne kadar (tahmini) zaman beklemeniz gerektiğini biliyorsanız faydalıdır

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

Swift: NSRunLoop.currentRunLoop().runMode(NSDefaultRunLoopMode, beforeDate: NSDate(timeIntervalSinceNow: <<time to wait in seconds>>))

Testinize devam etmek için bazı koşulları test etmeniz gerekiyorsa bu çok kullanışlı değildir. Koşullu denetimleri çalıştırmak için bir whiledöngü kullanın .


Bu özellikle temiz ve benim için çok kullanışlı, örneğin uygulama başlatmayı beklemek, önceden yüklenmiş verileri talep etmek ve giriş / çıkış yapmak. Teşekkür ederim.
felixwcf

4

Aşağıdaki kod yalnızca Objective C ile çalışır.

- (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];
}

Sadece bu işlevi aşağıda belirtildiği gibi arayın.

[self wait: 10];

Hata -> "NSInternalInconsistencyException", "API ihlali - herhangi bir beklenti belirlenmeden beklemeye çağrıldı."
FlowUI.

@ iOSCalendarpatchthecode.com, Bunun için alternatif bir çözüm buldunuz mu?
Maksimum

@ Max, bu sayfadaki diğer herhangi birini kullanabilir misiniz?
FlowUI. SimpleUITesting.com

@ iOSCalendarpatchthecode.com Hayır, kontrol etmek için herhangi bir öğe olmadan biraz gecikmeye ihtiyacım var. Yani bunun alternatifine ihtiyacım var.
Maksimum

@Max bu sayfada seçilen cevabı kullandım. Benim için çalıştı. Belki de onlara özellikle ne aradığınızı sorabilirsiniz.
FlowUI.

4

Benim durumumda sleepyan etki yarattım, bu yüzden kullandımwait

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

0

XCUIElement için API'ye göre .existsbir sorgu olup olmadığını kontrol etmek için kullanılabilir, bu nedenle aşağıdaki sözdizimi bazı durumlarda yararlı olabilir!

let app = XCUIApplication()
app.launch()

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

Beklentinizin eninde sonunda karşılanacağından eminseniz, bunu çalıştırmayı deneyebilirsiniz. Beklemenin çok uzun olması durumunda waitForExpectationsWithTimeout(_,handler:_)çökmenin tercih edilebileceği unutulmamalıdır; bu durumda @Joe Masilotti'nin gönderisinden kullanılmalıdır.


0

uyku ipliği engeller

"İş parçacığı engellendiğinde çalışma döngüsü işleme gerçekleşmez."

waitForExistence'ı kullanabilirsiniz

let app = XCUIApplication()
app.launch()

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

0

Bu, iş parçacığını uyku moduna geçirmeden veya zaman aşımı sırasında bir hata atmadan bir gecikme oluşturur:

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

Beklenti tersine döndüğü için sessizce zaman aşımına uğrar.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.