[ios] Xcode UI 테스트의 테스트 케이스에서 지연 / 대기

Xcode 7 베타 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)

UI 테스팅에 대한 자세한 내용은 내 블로그에서 확인할 수 있습니다.


답변

또한 잠을 잘 수 있습니다.

sleep(10)

UITest는 다른 프로세스에서 실행되므로 작동합니다. 그것이 얼마나 바람직한 지 모르겠지만 작동합니다.


답변

iOS 11 / Xcode 9

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

이것은이 사이트의 모든 커스텀 구현을 대체합니다!

https://stackoverflow.com/a/48937714/971329 에서 내 대답을 확인하십시오 . 테스트를 실행하는 시간을 크게 단축시키는 요청을 기다리는 대안을 설명합니다!


답변

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

용법

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)

출처


답변

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


답변

를 기반으로 테드의 대답 @ ,이 확장 기능을 사용했습니다 :

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


답변

편집하다:

실제로 Xcode 7b4에서 UI 테스트는 이제
expectationForPredicate:evaluatedWithObject:handler:

실물:

다른 방법은 설정된 시간 동안 런 루프를 돌리는 것입니다. 기다려야 할 시간 (예상) 시간을 알고있는 경우에만 유용합니다.

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루프를 사용하십시오 .