UITestHelper
一般信息
当创建 UI 测试时,您会发现在很多情况下您会重复相同的代码片段。UITestHelper 库将尝试减少这种情况。此外,还提供了一些功能,可以提高代码对错误标识符的抵抗力。
在自己的应用中使用 UITestHelper
'UITestHelper' 通过依赖管理器 CocoaPods 提供。您必须使用 CocoaPods 版本 1.1 或更高版本。
下面是一个示例 Podfile。您需要在 UI 测试目标中添加 UITestHelper pod,如果想要使用 GlobalHelper 函数,还必须将 UITestHelper/App pod 添加到您的应用目标中。
source 'https://github.com/CocoaPods/Specs.git'
use_frameworks!
workspace 'UITestHelper'
target 'UITestHelperApp' do
platform :ios, '9.0'
pod 'UITestHelper/App'
end
target 'UITestHelperUITests' do
platform :ios, '9.0'
pod 'UITestHelper'
end
# This will make sure your test project has ENABLE_BITCODE set to NO
post_install do |installer|
installer.pods_project.targets.each do |target|
if ['UITestHelperUITests'].include? target.name
target.build_configurations.each do |config|
config.build_settings['ENABLE_BITCODE'] = 'NO'
end
end
end
end
指导
通过 .tryLaunch 启动并连接到您的应用
如果您使用的是 XCode 构建服务器并在模拟器上运行测试,那么可能会发生服务器过于繁忙导致您无法连接到模拟器的情况。为此,tryLaunch 函数内置了重试机制。默认情况下,它将重试 10 次(可以通过参数进行修改)。在 tryLaunch 之后,测试类中的所有测试都将有一个 app 变量,它指向已启动的 XCUIApplication。
tryLaunch 函数还接受一个 RawRepresentable 元素数组。通常这将是具有 String 作为其基础类型的 Enum。您的应用将与这些参数启动。在您的应用中,您可以对这些参数作出反应。在下面的例子中,可以看到应用是带有 MockNetworkResponses 参数启动的。然后,您的应用可以使用 isLaunchedWith 函数检测该值并启动网络模拟。
// In your XCTestCase class:
override func setUp() {
super.setUp()
self.tryLaunch([LaunchArguments.MockNetworkResponses])
}
// Somewhere in your application (if you have added the UITestHelper/App pod)
if isLaunchedWith(LaunchArguments.MockNetworkResponses) {
// Start the network mocking
}
分组您的测试语句
为了使您的测试输出生成清晰的概述,您可以分组语句。组可以嵌套到您喜欢的程度。开始一个组就像
group("Testing the switch") { activity in
// Your test statements here will be grouped nicely in the test output.
}
捕获屏幕截图
屏幕截图将始终添加到活动组中。如果您没有参数调用 takeScreenshot 函数,则它将仅创建默认屏幕截图组。您也可以指定 groupName,在创建新组时使用。您也可以为屏幕截图命名。
group("Testing the switch") { activity in
takeScreenshot(activity: activity, "First screenshot")
takeScreenshot()
takeScreenshot(groupName: "Screenshot group?")
takeScreenshot("Last screenshot")
}
使用枚举作为标识符
UITestHelper 为 RawRepresentalbe 提供了一个扩展,以便您可以直接从 enum 值获取 XCElement,前提是您也将该 enum 值设置为无障碍标识符。例如,您可以创建一个嵌套的 enum 如此
enum AccessibilityIdentifier {
enum HomeScreen: String {
case theLabel
case theTextField
case theButton
case switch1
case switch2
case showButton
case hideButton
}
}
在您的 UIViewControllers 中,您可以将其设置了元素的访问标识符,如下所示
override func viewDidLoad() {
super.viewDidLoad()
theLabel ~~> AccessibilityIdentifier.HomeScreen.theLabel
theTextField ~~> AccessibilityIdentifier.HomeScreen.theTextField
theButton ~~> AccessibilityIdentifier.HomeScreen.theButton
switch1 ~~> AccessibilityIdentifier.HomeScreen.switch1
switch2 ~~> AccessibilityIdentifier.HomeScreen.switch2
hideButton ~~> AccessibilityIdentifier.HomeScreen.hideButton
showButton ~~> AccessibilityIdentifier.HomeScreen.showButton
}
在您的测试中,您可以使用这些内容:
func testEnumWithIdentifiersReusedForInteractingWithXCUIElement() {
HomeScreen.showButton.tap()
HomeScreen.hideButton.tap()
}
当标识符被重复使用时,例如在集合或表中,您可以枚举枚举元素。
// pressing the last theButton
let i = HomeScreen.theButton.count
HomeScreen.theButton[i - 1].tap()
}
然后您可以直接在枚举值上使用以下所有函数。
等待一个元素
当创建测试时,通常需要考虑元素是否可访问。为此,创建了各种辅助函数。app 将是您启动的 XCUIApplication。默认情况下,此功能最多将等待 10 秒。这可以通过参数来更改。waitUntilExists 将返回元素,但它可能仍然不可用 (.exists = false)。如果您想根据该存在性进行断言,则可以使用 .waitUntilExistsAssert 函数。
// Using the enum:
XCTAssert(HomeScreen.theLabel.waitUntilExists().exists, "label should exist")
HomeScreen.theLabel.waitUntilExistsAssert()
// Or just accessing the elements the old fashioned way:
app.buttons["Second"].waitUntilExists().tap()
app.buttons["Button"].waitUntilExists(3).tap()
选择存在的元素
使用 .or 函数,您可以获取 2 个元素中的任意一个。如果两个都不存在,则返回第一个元素,并且 .exists 将为 false。如果您想在这种情况下使用断言,则可以使用 .orAssert 函数。
HomeScreen.theLabel.or(HomeScreen.theTextfield).tap()
HoeScreen.theLabel.orAssert(HomeScreen.theTextfield)
根据存在性条件编写代码
如果元素存在,则执行一些代码。默认等待时间也为 10 秒,可以设置为参数。
// Only execute the closure if the element is there.
HomeScreen.theButton.ifExists { $0.tap() } // The button exist, so we do tap it
HomeScreen.hideButton.ifExists(2) { $0.tap() }
如果元素不存在,则执行一些代码。默认等待时间也为 10 秒,可以设置为参数。
// The button does not exist, so we don't tap it, we do something else
app.alerts.buttons["Hide"].ifNotExist(2) {
app.buttons["Third"].waitUntilExists().tap()
}
如果元素不存在,并且在代码假设执行后将存在。在下面的代码中,如果隐藏按钮不存在,则按显示按钮,这将使隐藏按钮出现,然后按下隐藏按钮。
HomeScreen.hideButton.ifNotExistwaitUntilExists(2) {
HomeScreen.showButton.waitUntilExists().tap()
}.tap()
}
在文本字段中输入字段
确保文本字段已存在,然后触摸它并输入文本。
HomeScreen.theTextField.tapAndType("testing")
开关通断
无论开关当前状态如何,它将被切换到指定的状态。
HomeScreen.switch1.setSwitch(false)
许可协议
UITestHelper 可在 MIT 3 许可下使用。详情请参阅 LICENSE 文件。
我的其他库
也请查看我的其他公开的 iOS 库
- EVReflection - 基于反射的对象映射(Dictionary、CKRecord、JSON 和 XML),并扩展了 Alamofire 和 Moya,支持 RxSwift 或 ReactiveSwift
- EVCloudKitDao - 简化访问 Apple 的 CloudKit
- EVFaceTracker - 根据您的设备与您的面部之间的距离和角度计算,以模拟 3D 效果
- EVURLCache - 一个 NSURLCache 子类,用于处理所有使用 NSURLRequest 的网络请求
- AlamofireOauth2 - 使用 Alamofire 的 OAuth2 实现
- EVWordPressAPI - 使用 AlamofireOauth2、AlomofireJsonToObjects 和 UITestHelper 实现 WordPress (Jetpack) API(开发中)
- PassportScanner - 扫描护照的 MRZ 码并提取姓名、姓氏、护照号码、国籍、出生日期、到期日期和个人号码
- AttributedTextView - 最简单的方式创建一个支持多个链接(url、hashtags、提及)的 attributed UITextView
- UITestHelper - UI 测试辅助函数