webentwicklung-frage-antwort-db.com.de

Xcode 7 UI Testing: Wie kann man eine Reihe von Systemwarnungen im Code verwerfen?

Ich schreibe UI-Testfälle mit der neuen Xcode 7-UI-Testfunktion. Zu einem bestimmten Zeitpunkt in meiner App frage ich den Benutzer nach der Erlaubnis für den Kamerazugriff und die Push-Benachrichtigung. Es werden also zwei iOS-Popups angezeigt: "MyApp Would Like to Access the Camera" popup und "MyApp Would Like to Send You Notifications" popup. Ich möchte, dass mein Test beide Popups abweist.

Die Aufzeichnung der Benutzeroberfläche hat folgenden Code für mich generiert: 

[app.alerts[@"cameraAccessTitle"].collectionViews.buttons[@"OK"] tap];

[app.alerts[@"cameraAccessTitle"] exists] wird jedoch in false aufgelöst und der obige Code generiert einen Fehler: Assertion Failure: UI Testing Failure - Failure getting refresh snapshot Error Domain=XCTestManagerErrorDomain Code=13 "Error copying attributes -25202"

Was ist also der beste Weg, um einen Stapel von Systemwarnungen im Test abzuweisen? Die System-Popups unterbrechen meinen App-Flow und schlagen sofort meine normalen UI-Testfälle fehl. In der Tat sind alle Empfehlungen, wie ich die Systemwarnungen umgehen kann, so dass ich mit dem Testen des üblichen Flusses fortfahren kann, sehr willkommen. 

Diese Frage könnte mit diesem SO -Posten zusammenhängen, der auch keine Antwort hat: Xcode7 | Xcode-UI-Tests | Umgang mit Standortdienstalarm

Danke im Voraus.

49
SeaJelly

Xcode 7.1

Xcode 7.1 hat das Problem mit Systemwarnungen behoben. Es gibt jedoch zwei kleine Fallstricke.

Zunächst müssen Sie einen "UI-Interruptions-Handler" einrichten, bevor Sie die Warnung ausgeben. Auf diese Weise teilen wir dem Framework mit, wie eine Warnung behandelt werden soll, wenn sie erscheint.

Zweitens müssen Sie nach der Anzeige der Warnung mit der Schnittstelle interagieren. Ein einfaches Tippen auf die App funktioniert einwandfrei, ist jedoch erforderlich.

addUIInterruptionMonitorWithDescription("Location Dialog") { (alert) -> Bool in
    alert.buttons["Allow"].tap()
    return true
}

app.buttons["Request Location"].tap()
app.tap() // need to interact with the app for the handler to fire

Der "Positionsdialog" ist nur eine Zeichenfolge, mit der der Entwickler feststellen kann, auf welchen Handler zugegriffen wurde. Er ist nicht spezifisch für den Alarmtyp.

Ich glaube, dass die Rückgabe von true vom Handler als "abgeschlossen" markiert wird, was bedeutet, dass es nicht erneut aufgerufen wird. Für Ihre Situation würde ich versuchen, false zurückzugeben, damit der zweite Alarm den Handler erneut auslöst.

Xcode 7.0

Im Folgenden wird ein einzelner "Systemalarm" in Xcode 7 Beta 6 verworfen:

let app = XCUIApplication()
app.launch()
// trigger location permission dialog

app.alerts.element.collectionViews.buttons["Allow"].tap()

Beta 6 hat eine Reihe von Korrekturen für UI-Tests eingeführt, und ich glaube, dass dies einer von ihnen war.

Beachten Sie auch, dass ich -element direkt über -alerts anrufe. Der Aufruf von -element für eine XCUIElementQuery zwingt das Framework, das übereinstimmende "einzige" Element auf dem Bildschirm auszuwählen. Dies funktioniert hervorragend für Warnungen, bei denen jeweils nur eine angezeigt werden kann. Wenn Sie dies jedoch für ein Label versuchen und zwei Labels haben, löst das Framework eine Ausnahme aus.

47
Joe Masilotti

Meine Güte. Es tippt immer auf "Nicht zulassen", obwohl ich absichtlich auf "Erlauben" tippe.

Wenigstens 

if app.alerts.element.collectionViews.buttons["Allow"].exists {
    app.tap()
}

erlaubt mir, weiterzumachen und andere Tests durchzuführen.

Ziel c

-(void) registerHandlerforDescription: (NSString*) description {

    [self addUIInterruptionMonitorWithDescription:description handler:^BOOL(XCUIElement * _Nonnull interruptingElement) {

        XCUIElement *element = interruptingElement;
        XCUIElement *allow = element.buttons[@"Allow"];
        XCUIElement *ok = element.buttons[@"OK"];

        if ([ok exists]) {
            [ok tap];
            return YES;
        }

        if ([allow exists]) {
            [allow tap];
            return YES;
        }

        return NO;
    }];
}

-(void)setUp {

    [super setUp];

    self.continueAfterFailure = NO;
    self.app = [[XCUIApplication alloc] init];
    [self.app launch];

    [self registerHandlerforDescription:@"“MyApp” would like to make data available to nearby Bluetooth devices even when you're not using app."];
    [self registerHandlerforDescription:@"“MyApp” Would Like to Access Your Photos"];
    [self registerHandlerforDescription:@"“MyApp” Would Like to Access the Camera"];
}

Schnell

addUIInterruptionMonitorWithDescription("Description") { (alert) -> Bool in
    alert.buttons["Allow"].tap()
    alert.buttons["OK"].tap()
    return true
}
3

Für diejenigen, die nach spezifischen Beschreibungen für bestimmte Systemdialogfelder suchen (wie ich es getan habe), gibt es keine :) Die Zeichenfolge dient nur zu Testverfolgungszwecken. Zugehöriger Apple-Dokumentlink: https://developer.Apple.com/documentation/xctest/xctestcase/1496273-adduiinterruptionmonitor


Update: xcode 9.2

Die Methode wird manchmal nicht ausgelöst. Die beste Lösung für mich ist, wenn ich weiß, dass es eine Systemwarnung gibt, füge ich hinzu: 

sleep(2)
app.tap()

und Systemalarm ist weg

1
ergunkocak

Das einzige, was ich zuverlässig gefunden habe, war die Einrichtung von zwei separaten Tests, um die Alarme zu bearbeiten. Im ersten Test rufe ich app.tap() auf und tue nichts anderes. Im zweiten Test rufe ich erneut app.tap() auf und erledige dann die eigentliche Arbeit.

1
Kevin London

Gott! Ich hasse es, dass XCTest die schlechteste Zeit im Umgang mit UIView Alerts hat. Ich habe eine App, bei der ich zwei Warnmeldungen bekomme. Zuerst muss ich "Zulassen" auswählen, um Standortdienste für App-Berechtigungen zu aktivieren. Anschließend muss der Benutzer auf einer Begrüßungsseite ein UIButton mit dem Namen "Standort einschalten" drücken und schließlich eine Benachrichtigungs-SMS-Benachrichtigung in einem UIViewAlert und der Benutzer muss "OK" auswählen. Das Problem, das wir hatten, war nicht in der Lage, mit dem System Alerts zu interagieren, sondern auch eine Racebedingung, bei der das Verhalten und sein Erscheinungsbild auf dem Bildschirm nicht rechtzeitig waren. Wenn Sie den alert.element.buttons["whateverText"].tap verwenden, scheint die Logik von XCTest so lange zu drücken, bis die Zeit des Tests abgelaufen ist. Drücken Sie also im Wesentlichen auf den Bildschirm, bis alle Systemwarnungen sichtbar sind.

Dies ist ein Hack, aber das hat bei mir funktioniert.

    func testGetPastTheStupidAlerts(){
    let app = XCUIApplication()
    app.launch()

    if app.alerts.element.collectionViews.buttons["Allow"].exists {
        app.tap()
    }

    app.buttons["TURN ON MY LOCATION"].tap()
}

Die Zeichenfolge "Allow" wird vollständig ignoriert und die Logik für app.tap() heißt "evreytime", wenn ein Alarm angezeigt wird, und schließlich ist der Button, den ich erreichen wollte, und der Test bestanden 

~ Total verwirrt, danke Apple.

1
JJacquet

Auf xcode 9.1 werden Warnmeldungen nur verarbeitet, wenn das Testgerät über iOS 11 verfügt. Funktioniert nicht mit älteren iOS-Versionen, z. B. 10.3 usw. Referenz: https://forums.developer.Apple.com/thread/86989

Um mit Alerts umzugehen, verwenden Sie folgendes:

//Use this before the alerts appear. I am doing it before app.launch()

let allowButtonPredicate = NSPredicate(format: "label == 'Always Allow' || label == 'Allow'")
//1st alert
_ = addUIInterruptionMonitor(withDescription: "Allow to access your location?") { (alert) -> Bool in
    let alwaysAllowButton = alert.buttons.matching(allowButtonPredicate).element.firstMatch
    if alwaysAllowButton.exists {
        alwaysAllowButton.tap()
        return true
    }
    return false
}
//Copy paste if there are more than one alerts to handle in the app
0
Hasaan Ali

@Joe Masilottis Antwort ist richtig und danke, das hat mir sehr geholfen :)

Ich möchte nur auf die eine Sache hinweisen, und zwar den UIInterruptionMonitor fängt alle Systemalarme, die in SerieZUSAMMENdargestellt werden, sodass die Aktion, die Sie im Beendigungshandler anwenden, erhalten wird wird auf jeden Alarm angewendet ("Nicht zulassen" oder "OK"). Wenn Sie Alert-Aktionen unterschiedlich behandeln möchten, müssen Sie im Completion-Handler prüfen, welche Alert aktuell angezeigt wird, z. durch Überprüfen des statischen Textes wird die Aktion nur auf diese Warnung angewendet.

Hier ist ein kleines Code-Snippet zum Anwenden der Aktion "Nicht zulassen" bei der zweiten Warnung, in einer Serie von drei Warnungen und "OK" bei den verbleibenden zwei:

addUIInterruptionMonitor(withDescription: "Access to sound recording") { (alert) -> Bool in
        if alert.staticTexts["MyApp would like to use your microphone for recording your sound."].exists {
            alert.buttons["Don’t Allow"].tap()
        } else {
            alert.buttons["OK"].tap()
        }
        return true
    }
app.tap()
0
bra.Scene