Vertraging/Wacht in een testcase van Xcode UI-testen

Ik probeer een testcase te schrijven met behulp van de nieuwe UI Testing die beschikbaar is in Xcode 7 beta 2. De app heeft een inlogscherm waar hij naar de server belt om in te loggen. Er is een vertraging aan verbonden omdat het een asynchrone bewerking is.

Is er een manier om een ​​vertragings- of wachtmechanisme in de XCTestCase te veroorzaken voordat verder wordt gegaan met verdere stappen?

Er is geen goede documentatie beschikbaar en ik heb de Header-bestanden van de klassen doorgenomen. Kon hier niets over vinden.

Ideeën/suggesties?


Antwoord 1, autoriteit 100%

Bovendien kun je gewoon slapen:

sleep(10)

Omdat de UITests in een ander proces draaien, werkt dit. Ik weet niet hoe raadzaam het is, maar het werkt.


Antwoord 2, autoriteit 74%

Asynchrone UI-testen is geïntroduceerd in Xcode 7 Beta 4. Wachten op een label met de tekst “Hallo, wereld!” om te verschijnen kunt u het volgende doen:

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)

Meer details over UI-testenzijn te vinden op mijn blog.


Antwoord 3, autoriteit 38%

iOS 11 / Xcode 9

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

Dit is een geweldige vervanging voor alle aangepaste implementaties op deze site!

Bekijk mijn antwoord hier: https://stackoverflow.com/a/48937714/971329. Daar beschrijf ik een alternatief voor het wachten op verzoeken, waardoor de tijd dat uw tests worden uitgevoerd aanzienlijk wordt verkort!


Antwoord 4, autoriteit 4%

Bewerken:

Het viel me eigenlijk net op dat in Xcode 7b4, UI-testen nu heeft
expectationForPredicate:evaluatedWithObject:handler:

Origineel:

Een andere manier is om de loop voor een bepaalde tijd te laten draaien. Echt alleen handig als je weet hoeveel (geschatte) tijd je moet wachten

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

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

Dit is niet erg handig als u bepaalde voorwaarden moet testen om door te gaan met uw test. Gebruik een while-lus om voorwaardelijke controles uit te voeren.


Antwoord 5, autoriteit 4%

Dit zal een vertraging veroorzaken zonder de thread in de sluimerstand te zetten of een foutmelding te geven bij time-out:

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

Omdat de verwachting omgekeerd is, zal deze stil verlopen.


Antwoord 6, autoriteit 3%

Xcode testen Wacht

In mijn geval veroorzaakte sleepeen bijwerking, dus gebruikte ik wait

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

Antwoord 7, autoriteit 2%

Hoe we het bij mijn huidige bedrijf doen, is dat we een XCUIElement-uitdrukkingsverwachting creëren (om een ​​veelzijdige wachtmethode te creëren). We doen het op de volgende manier om ervoor te zorgen dat het onderhoudbaar is (veel variatie in verwachtingen, en we willen niet veel methoden/specifieke predikaten maken om dit te doen.

Swift 5

Basismethode

De uitdrukking wordt gebruikt om een ​​dynamische predikaatwaarde te vormen. We kunnen XCTNSPredicateExpectation‘s maken van predikaten, die we vervolgens doorgeven aan XCTWaiterom expliciet te wachten. Als het resultaat iets anders was dan completed, dan falen we met een optioneel bericht.

@discardableResult
func wait(
    until expression: @escaping (XCUIElement) -> Bool,
    timeout: TimeInterval = 15,
    message: @autoclosure () -> String = "",
    file: StaticString = #file,
    line: UInt = #line
) -> Self {
    if expression(self) {
        return self
    }
    let predicate = NSPredicate { _, _ in
        expression(self)
    }
    let expectation = XCTNSPredicateExpectation(predicate: predicate, object: nil)
    let result = XCTWaiter().wait(for: [expectation], timeout: timeout)
    if result != .completed {
        XCTFail(
            message().isEmpty ? "expectation not matched after waiting" : message(),
            file: file,
            line: line
        )
    }
    return self
}

Gebruik

app.buttons["my_button"].wait(until: { $0.exists })
app.buttons["my_button"].wait(until: { $0.isHittable })

Toetsenpaden

Vervolgens verpakken we dat in een methode waarbij een keyPath en match-waarde de expressie vormen.

@discardableResult
func wait<Value: Equatable>(
    until keyPath: KeyPath<XCUIElement, Value>,
    matches match: Value,
    timeout: TimeInterval = 15,
    message: @autoclosure () -> String = "",
    file: StaticString = #file,
    line: UInt = #line
) -> Self {
    wait(
        until: { $0[keyPath: keyPath] == match },
        timeout: timeout,
        message: message,
        file: file,
        line: line
    )
}

Gebruik

app.buttons["my_button"].wait(until: \.exists, matches: true)
app.buttons["my_button"].wait(until: \.isHittable, matches: false)

Vervolgens kun je die methode inpakken, waarbij de waarde matchaltijd trueis voor een gebruik dat ik het meest heb gevonden.

Gebruik

app.buttons["my_button"].wait(until: \.exists)
app.buttons["my_button"].wait(until: \.isHittable)

Ik heb er een bericht over geschreven en krijg daar ook het volledige extensiebestand: https: //sourcediving.com/clean-waiting-in-xcutest-43bab495230f


Antwoord 8, autoriteit 2%

De volgende code werkt alleen met Objective C.

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

Bel gewoon deze functie aan zoals hieronder aangegeven.

[self wait: 10];

Antwoord 9

sleep blokkeert de thread

“Er vindt geen run loop-verwerking plaats terwijl de thread is geblokkeerd.”

u kunt waitForExistence gebruiken

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

Antwoord 10

Volgens de API voor XCUIElement kan .existsworden gebruikt om te controleren of een query bestaat of niet, dus de volgende syntaxis kan in sommige gevallen nuttig zijn!

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

Als u er zeker van bent dat uw verwachting uiteindelijk zal worden waargemaakt, kunt u proberen dit uit te voeren. Opgemerkt moet worden dat crashen de voorkeur heeft als het wachten te lang is, in welk geval waitForExpectationsWithTimeout(_,handler:_)uit de post van @Joe Masilotti moet worden gebruikt.


Antwoord 11

  let app = XCUIApplication()
    app.launch()
     //Find the button in the UI 
    let SettingsButton =
        app.navigationBars["HomeView"].buttons["Settings"]
    XCTAssertTrue(settingButton.waitForExistence(timeout: 10))

Other episodes