私はXcode 7 beta 2で利用可能な新しいUI Testingを使ってテストケースを書くことを試みています。それはログインするためにそれがサーバーに電話をかけるログインスクリーンを持っています。非同期操作であるため、これに関連した遅延があります。
さらなるステップに進む前にXCTestCaseで遅延または待機メカニズムを引き起こす方法はありますか?
利用可能な適切なドキュメンテーションがないし、私はクラスのヘッダファイルを調べました。これに関連するものを見つけることができませんでした。
何かアイデアや提案?
非同期UIテストは、Xcode 7 Beta 4で導入されました。「Hello、world!」というテキストのラベルを待ちます。表示されるようにするには、次の操作を行います。
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)
詳細 Iテストの詳細 私のブログで見つけることができます。
さらに、あなたはただ眠ることができます:
sleep(10)
UITestsは別のプロセスで実行されるため、これは機能します。それがどれほど賢明かはわかりませんが、うまくいきます。
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より前
目標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];
スウィフト
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)
または
let element = app.staticTexts["Name of your element"]
self.waitForElementToAppear(element, timeout: 10)
iOS 11/Xcode 9
<#yourElement#>.waitForExistence(timeout: 5)
これは、このサイトのすべてのカスタム実装に代わる素晴らしいものです。
ここに私の答えを見ていることを確認してください: https://stackoverflow.com/a/48937714/971329 。そこに私はあなたのテストが走っている時間を大いに減らすであろう要求を待つことに代わるものを記述します!
Xcode 8.3以降、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
}
もう一つのトリックはwait
関数を書くことです、クレジットは私にそれを見せてくれたためJohn Johndellに行きます
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)
}
編集:
Xcode 7b4では、UIテストにexpectationForPredicate:evaluatedWithObject:handler:
が追加されました。
オリジナル:
もう1つの方法は、一定時間実行ループを回転させることです。あなたが待つ必要がある(推定)時間の長さを知っている場合にのみ本当に役に立ちます
Obj-C:[[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
ループを使用します。
@ Ted's answer に基づいて、私はこの拡張を使いました:
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
次のコードは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];
}
下記のようにこの関数を呼び出すだけです。
[self wait: 10];
私の場合はsleep
が副作用を引き起こしたので、私は使いました
XCTWaiter.wait(for: [XCTestExpectation(description: "Hello World!")], timeout: 2.0)
XCUIElementのAPIによると、クエリが存在するかどうかを確認するために.exists
を使用することができるので、次の構文が役に立つ場合があります。
let app = XCUIApplication()
app.launch()
let label = app.staticTexts["Hello, world!"]
while !label.exists {
sleep(1)
}
あなたの期待が最終的に満たされると確信しているなら、あなたはこれを実行しようとすることができました。注意しなければならないのは、待ち時間が長すぎる場合はクラッシュすることが望ましいかもしれません。その場合は@Joe Masilottiの投稿からのwaitForExpectationsWithTimeout(_,handler:_)
を使用するべきです。