203

Xcode 7 ベータ 2 で利用可能な新しい UI Testing を使用してテスト ケースを作成しようとしています。アプリには、サーバーを呼び出してログインするログイン画面があります。これは非同期操作であるため、これに関連する遅延があります。

次の手順に進む前に、XCTestCase で遅延または待機メカニズムを発生させる方法はありますか?

利用可能な適切なドキュメントがなく、クラスのヘッダー ファイルを確認しました。これに関連するものは見つかりませんでした。

アイデア/提案はありますか?

4

14 に答える 14

93

iOS 11 / Xcode 9

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

これは、このサイトのすべてのカスタム実装の優れた代替品です!

ここで私の答えを必ず見てください: https://stackoverflow.com/a/48937714/971329。そこで、テストの実行時間を大幅に短縮する、リクエストを待機する代わりの方法について説明します。

于 2017-08-31T18:32:34.450 に答える
78

Xcode 9はXCTWaiter で新しいトリックを導入しました

テスト ケースは明示的に待機します

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

Waiter インスタンスがテストに委任する

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

利用方法

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

利用方法

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

また

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

ソース

于 2015-11-22T13:21:15.907 に答える
36

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
}

もう 1 つの秘訣は、wait関数を作成することです。これを見せてくれた John Sundell 氏の功績によるものです。

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)
}
于 2017-02-14T09:28:36.767 に答える
14

@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."
                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)
            }
        }
    }

}

こんな感じで使えます

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

また、要素が消えるのを待つことも、他のプロパティが変更されるのを待つこともできます (適切なブロックを使用して)

waitFor(object: element) { !$0.exists } // Wait for it to disappear
于 2017-01-12T10:34:52.753 に答える
10

編集:

実際、Xcode 7b4 では UI テストに expectationForPredicate:evaluatedWithObject:handler:

オリジナル:

もう 1 つの方法は、実行ループを一定時間スピンすることです。待つ必要がある(推定)時間がわかっている場合にのみ、本当に役立ちます

オブジェクト 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ループを使用します。

于 2015-07-22T13:09:18.980 に答える
8

Xcode のテスト 待機

私の場合sleep、副作用が発生したので、使用しましたwait

let _ = XCTWaiter.wait(for: [XCTestExpectation(description: "Hello World!")], timeout: 2.0)
于 2019-05-24T15:03:27.517 に答える
0

XCUIElement の API によると.exists、クエリが存在するかどうかを確認するために使用できるため、場合によっては次の構文が役立ちます。

let app = XCUIApplication()
app.launch()

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

最終的に期待が満たされると確信している場合は、これを実行してみてください。waitForExpectationsWithTimeout(_,handler:_)@Joe Masilotti の投稿を使用する必要がある場合は、待機が長すぎる場合にクラッシュが望ましいことに注意してください。

于 2016-07-18T20:22:20.267 に答える