IT story

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

hot-time 2020. 5. 27. 07:39
반응형

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는 다른 프로세스에서 실행되므로 작동합니다. 나는 그것이 얼마나 바람직한 지 모르겠지만 작동합니다.


Xcode 9XCTWaiter에 새로운 트릭을 도입 했습니다

테스트 케이스가 명시 적으로 대기

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)

출처


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

Edit:

It actually just occurred to me that in Xcode 7b4, UI testing now has expectationForPredicate:evaluatedWithObject:handler:

Original:

Another way is to spin the run loop for a set amount of time. Really only useful if you know how much (estimated) time you'll need to wait for

Obj-C: [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow: <<time to wait in seconds>>]]

Swift: NSRunLoop.currentRunLoop().runMode(NSDefaultRunLoopMode, beforeDate: NSDate(timeIntervalSinceNow: <<time to wait in seconds>>))

This is not super useful if you need to test some conditions in order to continue your test. To run conditional checks, use a while loop.


Based on @Ted's answer, I've used this 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."
                self.recordFailure(withDescription: message, inFile: file, atLine: line, expected: true)
            }
        }
    }

}

You can use it like this

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

It also allows for waiting for an element to disappear, or any other property to change (by using the appropriate block)

waitFor(object: element) { !$0.exists } // Wait for it to disappear

The following code just works with 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];
}

Just make call to this function as given below.

[self wait: 10];

In my case sleep created side effect so I used

XCTWaiter.wait(for: [XCTestExpectation(description: "Hello World!")], timeout: 2.0)

According to the API for XCUIElement .exists can be used to check if a query exists or not so the following syntax could be useful in some cases!

let app = XCUIApplication()
app.launch()

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

If you are confident that your expectation will be met eventually you could try running this. It should be noted that crashing might be preferable if the wait is too long in which case waitForExpectationsWithTimeout(_,handler:_) from @Joe Masilotti's post should be used.

참고URL : https://stackoverflow.com/questions/31182637/delay-wait-in-a-test-case-of-xcode-ui-testing

반응형