当 运行 使用并行测试同时进行所有测试时,输入文本字段不稳定
Inputting textfields is flaky when running all tests at once using parallel testing
我正在尝试使用 XCUITest
进行 UI 测试。目前我在将文本输入 UITextField
时遇到了一些问题。这是我在 Robot
class 中使用键盘输入文本的内容:
func typeTextToTextField(_ element: XCUIElement, text: String, timeout: TimeInterval = timeInterval,
file: StaticString, line: UInt) {
guard assertExists(element, timeout: timeout, file: file, line: line),
element.isHittable else {
return
}
element.tap()
sleep(2)
app.activate()
element.typeText("\(text)\n")
sleep(2)
}
我试过这些步骤才变成这样:
- 只有
element.tap()
和 element.typeText()
。
- 添加守卫等待存在(
assertExists()
块)
- 添加了
sleep()
等待键盘出现几秒钟。
- 添加了
app.activate()
因为显然有时查询花费的时间太长。关于这里错误的回答说我应该先调用 app.activate()。
如果我运行一次使用并行测试进行所有测试,一些使用它的测试会工作,但有些会失败。有些会正确显示键盘并正确键入文本,而另一些则无法显示键盘并显示此错误 Failed to synthesize event: Neither element nor any descendant has keyboard focus.
。但是当我 运行 一次测试一个时,它们都会是绿色的并且可以完美地工作。
因此,接下来,我尝试通过粘贴文本而不是键入来更改输入方式。那也行不通。这是代码:
func pasteToTextField(_ element: XCUIElement, text: String, timeout: TimeInterval = timeInterval,
file: StaticString, line: UInt) {
app.activate()
guard assertExists(element, timeout: timeout, file: file, line: line),
element.isHittable else {
return
}
UIPasteboard.general.string = text
app.activate()
element.tap()
element.doubleTap()
element.press(forDuration: 1.2)
tap(pasteMenuItem, timeout: timeout, file: file, line: line)
sleep(5)
}
所以我做到了:
- 只有
element.tap()
和tap(pasteMenuItem, timeout: timeout, file: file, line: line)
(与app.menuItems["Paste"].tap()
相同)
- 添加了
element.doubleTap()
- 添加了
element.press()
使用粘贴功能时,有时“粘贴”菜单项不显示并出现此错误:failed - Element: "Paste" MenuItem does not exist!
。但有时它工作得很好。
所以,我现在束手无策了。如何将文本输入到文本字段中,无论是 运行 所有测试还是 运行 单个测试用例,它都肯定能在所有条件下正常工作?
提前致谢。
PS:它在我的 CI 机器上工作得很好。真奇怪。
你可以试试这个
extension XCUIElement {
func clearAndEnterText(_ text: String, app: XCUIApplication) {
let currentClipboard = UIPasteboard.general.string ?? ""
waitForElementToBecomeHittable(timeout: .small)
guard let stringValue = value as? String else {
return XCTFail("Tried to clear and enter text into a non string value")
}
if stringValue == text {
return
}
if stringValue.isNotEmpty {
if app.isKeyboardKeysAvaliable(key: XCUIKeyboardKey.delete.rawValue) {
let deleteString = stringValue.map { _ in XCUIKeyboardKey.delete.rawValue }.joined()
typeText(deleteString)
} else {
let selectAllButton = app.menuItems.element(predicate: .label(Comparison.containsAny, "Select All")).firstMatch
if !selectAllButton.waitForExistence(timeout: .small) {
press(forDuration: 1.1)
}
if selectAllButton.waitForExistence(timeout: .small) {
selectAllButton.tapElement()
}
}
}
if value(forKey: "hasKeyboardFocus") as? Bool ?? false {
typeText(text)
} else {
UIPasteboard.general.string = text
let pasteButton = app.menuItems.element(predicate: .label(Comparison.containsAny, "Paste"))
if !pasteButton.waitForExistence(timeout: .small) {
tap()
press(forDuration: 2.1)
}
if pasteButton.waitForExistence(timeout: .small) {
pasteButton.tapElement()
}
}
// Dismiss tooltip if it is still displayed
if app.menuItems.element.waitForExistence(timeout: .small) {
app.tap()
}
waitForElementToBecomeHittable(timeout: .small)
UIPasteboard.general.string = currentClipboard
}
}
注意:您需要导入此文件 https://github.com/ZhipingYang/Einstein/blob/600854f9b6f93bb3694deddb3fb6edbae0f67f74/Class/UITest/Model/EasyPredicate.swift,而且我已将名称从 EasyPredicate
更改为 Predicate
extension XCUIApplication {
func isKeyboardKeysAvaliable(key: String) -> Bool {
let keyboard = keyboards.element(boundBy: 0)
if key.contains(all: "next") {
return keyboard.buttons[key].exists
} else {
return keyboard.keys[key].exists
}
}
}
extension XCUIElement {
@discardableResult
func waitForElementToBecomeHittable(timeout: Timeout) -> Bool {
return waitForExistence(timeout: timeout) && isHittable
}
我正在尝试使用 XCUITest
进行 UI 测试。目前我在将文本输入 UITextField
时遇到了一些问题。这是我在 Robot
class 中使用键盘输入文本的内容:
func typeTextToTextField(_ element: XCUIElement, text: String, timeout: TimeInterval = timeInterval,
file: StaticString, line: UInt) {
guard assertExists(element, timeout: timeout, file: file, line: line),
element.isHittable else {
return
}
element.tap()
sleep(2)
app.activate()
element.typeText("\(text)\n")
sleep(2)
}
我试过这些步骤才变成这样:
- 只有
element.tap()
和element.typeText()
。 - 添加守卫等待存在(
assertExists()
块) - 添加了
sleep()
等待键盘出现几秒钟。 - 添加了
app.activate()
因为显然有时查询花费的时间太长。关于这里错误的回答说我应该先调用 app.activate()。
如果我运行一次使用并行测试进行所有测试,一些使用它的测试会工作,但有些会失败。有些会正确显示键盘并正确键入文本,而另一些则无法显示键盘并显示此错误 Failed to synthesize event: Neither element nor any descendant has keyboard focus.
。但是当我 运行 一次测试一个时,它们都会是绿色的并且可以完美地工作。
因此,接下来,我尝试通过粘贴文本而不是键入来更改输入方式。那也行不通。这是代码:
func pasteToTextField(_ element: XCUIElement, text: String, timeout: TimeInterval = timeInterval,
file: StaticString, line: UInt) {
app.activate()
guard assertExists(element, timeout: timeout, file: file, line: line),
element.isHittable else {
return
}
UIPasteboard.general.string = text
app.activate()
element.tap()
element.doubleTap()
element.press(forDuration: 1.2)
tap(pasteMenuItem, timeout: timeout, file: file, line: line)
sleep(5)
}
所以我做到了:
- 只有
element.tap()
和tap(pasteMenuItem, timeout: timeout, file: file, line: line)
(与app.menuItems["Paste"].tap()
相同) - 添加了
element.doubleTap()
- 添加了
element.press()
使用粘贴功能时,有时“粘贴”菜单项不显示并出现此错误:failed - Element: "Paste" MenuItem does not exist!
。但有时它工作得很好。
所以,我现在束手无策了。如何将文本输入到文本字段中,无论是 运行 所有测试还是 运行 单个测试用例,它都肯定能在所有条件下正常工作?
提前致谢。
PS:它在我的 CI 机器上工作得很好。真奇怪。
你可以试试这个
extension XCUIElement {
func clearAndEnterText(_ text: String, app: XCUIApplication) {
let currentClipboard = UIPasteboard.general.string ?? ""
waitForElementToBecomeHittable(timeout: .small)
guard let stringValue = value as? String else {
return XCTFail("Tried to clear and enter text into a non string value")
}
if stringValue == text {
return
}
if stringValue.isNotEmpty {
if app.isKeyboardKeysAvaliable(key: XCUIKeyboardKey.delete.rawValue) {
let deleteString = stringValue.map { _ in XCUIKeyboardKey.delete.rawValue }.joined()
typeText(deleteString)
} else {
let selectAllButton = app.menuItems.element(predicate: .label(Comparison.containsAny, "Select All")).firstMatch
if !selectAllButton.waitForExistence(timeout: .small) {
press(forDuration: 1.1)
}
if selectAllButton.waitForExistence(timeout: .small) {
selectAllButton.tapElement()
}
}
}
if value(forKey: "hasKeyboardFocus") as? Bool ?? false {
typeText(text)
} else {
UIPasteboard.general.string = text
let pasteButton = app.menuItems.element(predicate: .label(Comparison.containsAny, "Paste"))
if !pasteButton.waitForExistence(timeout: .small) {
tap()
press(forDuration: 2.1)
}
if pasteButton.waitForExistence(timeout: .small) {
pasteButton.tapElement()
}
}
// Dismiss tooltip if it is still displayed
if app.menuItems.element.waitForExistence(timeout: .small) {
app.tap()
}
waitForElementToBecomeHittable(timeout: .small)
UIPasteboard.general.string = currentClipboard
}
}
注意:您需要导入此文件 https://github.com/ZhipingYang/Einstein/blob/600854f9b6f93bb3694deddb3fb6edbae0f67f74/Class/UITest/Model/EasyPredicate.swift,而且我已将名称从 EasyPredicate
更改为 Predicate
extension XCUIApplication {
func isKeyboardKeysAvaliable(key: String) -> Bool {
let keyboard = keyboards.element(boundBy: 0)
if key.contains(all: "next") {
return keyboard.buttons[key].exists
} else {
return keyboard.keys[key].exists
}
}
}
extension XCUIElement {
@discardableResult
func waitForElementToBecomeHittable(timeout: Timeout) -> Bool {
return waitForExistence(timeout: timeout) && isHittable
}