J'essaie d'écrire un cas de test en utilisant le nouveau test d'interface utilisateur disponible dans Xcode 7 beta 2. L'application dispose d'un écran de connexion où elle appelle le serveur pour se connecter. Il y a un délai associé à cela car il s'agit d'une opération asynchrone.
Existe-t-il un moyen de provoquer un retard ou un mécanisme d'attente dans XCTestCase avant de passer aux étapes suivantes?
Il n'y a pas de documentation appropriée disponible et j'ai parcouru les fichiers d'en-tête des classes. Je n'ai rien pu trouver à ce sujet.
Des idées / suggestions?
ios
ios9
xcode-ui-testing
xcode7-beta2
xctwaiter
Tejas HS
la source
la source
NSThread.sleepForTimeInterval(1)
devrait fonctionnerdispatch_after
,dispatch_queue
stuff)Réponses:
Le test asynchrone de l'interface utilisateur a été introduit dans Xcode 7 Beta 4. Attendre une étiquette avec le texte "Hello, world!" pour apparaître, vous pouvez faire ce qui suit:
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)
Plus de détails sur les tests d'interface utilisateur peuvent être trouvés sur mon blog.
la source
waitForExpectationsWithTimeout
échouera automatiquement votre test, ce qui est assez regrettable.app.launch()
semble juste relancer l'application. Est-ce nécessaire?De plus, vous pouvez simplement dormir:
sleep(10)
Puisque les tests UIT sont exécutés dans un autre processus, cela fonctionne. Je ne sais pas à quel point c'est souhaitable, mais ça marche.
la source
usleep
iOS 11 / Xcode 9
<#yourElement#>.waitForExistence(timeout: 5)
C'est un excellent remplacement pour toutes les implémentations personnalisées sur ce site!
Assurez-vous de jeter un œil à ma réponse ici: https://stackoverflow.com/a/48937714/971329 . Là, je décris une alternative à l'attente de demandes qui réduira considérablement le temps d'exécution de vos tests!
la source
XCTestCase
et cela fonctionne comme un charme. Je ne comprends pas pourquoi des approches commesleep(3)
sont votées si haut ici car cela prolonge artificiellement le temps de test et n'est vraiment pas une option lorsque votre suite de tests se développe.Xcode 9 a introduit de nouvelles astuces avec XCTWaiter
Le cas de test attend explicitement
wait(for: [documentExpectation], timeout: 10)
Délégués d'instance de serveur pour tester
XCTWaiter(delegate: self).wait(for: [documentExpectation], timeout: 10)
La classe de serveur renvoie le résultat
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 }
utilisation de l'échantillon
Avant Xcode 9
Objectif 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]; } }]; }
USAGE
XCUIElement *element = app.staticTexts["Name of your element"]; [self waitForElementToAppear:element withTimeout:5];
Rapide
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) } } }
USAGE
let element = app.staticTexts["Name of your element"] self.waitForElementToAppear(element)
ou
let element = app.staticTexts["Name of your element"] self.waitForElementToAppear(element, timeout: 10)
LA SOURCE
la source
Depuis Xcode 8.3, nous pouvons utiliser
XCTWaiter
http://masilotti.com/xctest-waiting/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 }
Une autre astuce consiste à écrire une
wait
fonction, le mérite revient à John Sundell de me l'avoir montré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) } }
et utilisez-le comme
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) }
la source
Sur la base de la réponse de @ Ted , j'ai utilisé cette extension:
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." let location = XCTSourceCodeLocation(filePath: file, lineNumber: line) let issue = XCTIssue(type: .assertionFailure, compactDescription: message, detailedDescription: nil, sourceCodeContext: .init(location: location), associatedError: nil, attachments: []) self.record(issue) } } } }
Vous pouvez l'utiliser comme ça
let element = app.staticTexts["Name of your element"] waitFor(object: element) { $0.exists }
Il permet également d'attendre la disparition d'un élément ou de modifier toute autre propriété (en utilisant le bloc approprié)
waitFor(object: element) { !$0.exists } // Wait for it to disappear
la source
Éditer:
Il m'est venu à l'esprit que dans Xcode 7b4, les tests d'interface utilisateur ont maintenant
expectationForPredicate:evaluatedWithObject:handler:
Original:
Une autre méthode consiste à faire tourner la boucle d'exécution pendant une durée définie. Vraiment utile uniquement si vous savez combien de temps (estimé) vous devrez attendre
Obj-C:
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow: <<time to wait in seconds>>]]
Rapide:
NSRunLoop.currentRunLoop().runMode(NSDefaultRunLoopMode, beforeDate: NSDate(timeIntervalSinceNow: <<time to wait in seconds>>))
Ce n'est pas très utile si vous devez tester certaines conditions afin de continuer votre test. Pour exécuter des vérifications conditionnelles, utilisez une
while
boucle.la source
Dans mon cas, j'ai
sleep
créé un effet secondaire alors j'ai utiliséwait
let _ = XCTWaiter.wait(for: [XCTestExpectation(description: "Hello World!")], timeout: 2.0)
la source
Le code suivant fonctionne uniquement avec 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]; }
Appelez simplement cette fonction comme indiqué ci-dessous.
[self wait: 10];
la source
Cela créera un délai sans mettre le thread en veille ou générer une erreur à l'expiration du délai:
let delayExpectation = XCTestExpectation() delayExpectation.isInverted = true wait(for: [delayExpectation], timeout: 5)
Parce que l'attente est inversée, elle expirera silencieusement.
la source
Selon l'API pour XCUIElement
.exists
peut être utilisé pour vérifier si une requête existe ou non donc la syntaxe suivante pourrait être utile dans certains cas!let app = XCUIApplication() app.launch() let label = app.staticTexts["Hello, world!"] while !label.exists { sleep(1) }
Si vous êtes convaincu que vos attentes seront éventuellement satisfaites, vous pouvez essayer de l'exécuter. Il convient de noter que le crash peut être préférable si l'attente est trop longue, auquel cas le
waitForExpectationsWithTimeout(_,handler:_)
message de @Joe Masilotti doit être utilisé.la source
le sommeil bloquera le fil
"Aucun traitement de boucle d'exécution ne se produit pendant que le thread est bloqué."
vous pouvez utiliser waitForExistence
let app = XCUIApplication() app.launch() if let label = app.staticTexts["Hello, world!"] { label.waitForExistence(timeout: 5) }
la source