概述
强大的针对 iOS 的端到端 UI 测试框架。
目前它在 Avito 中使用,我们已经有 700+ 的端到端 UI 测试,其中约 90% 为绿色,帮助我们减少了 2 年以来的手动测试。我们大约在 PR 上运行 25%,我们正在努力使 100% 的测试在 pull request 上运行,部分是通过从黑盒端到端过渡到灰盒测试。我们在 3 个平台上运行这些测试,大约需要 40 分钟(测试总时长为 30+ 小时),因为我们使用了 Emcee,这是一个在多台机器上运行测试的测试运行器(注意,Mixbox 不需要 Emcee)。我们也正在编写灰盒测试(我们在其中模拟类、网络等一切),但我们刚开始。
如果您想在公司中使用它,给我们提交一个问题。我们使之能够被社区使用,然而,这不是我们的主要目标。
特性
-
行为和服务(当然)
-
像素可见性检查
-
一切都有轮询
-
全自动滚动
-
黑盒和灰盒测试!
- 黑盒测试在独立的应用程序中运行,并且能够启动应用程序。您可以使用这种类型的测试测试更多应用程序功能。您可以使用模拟,但这将要求您编写更多代码(使用启动参数或实现进程间通信)。
- 灰盒测试在应用程序内部运行(如在EarlGrey中所示),但测试与黑盒测试兼容性强,因此您可以共享测试模型、页面对象、测试辅助程序等。由于测试在同一个进程中执行,因此可以轻松地对任何内容进行模拟。您不能不使用模拟来测试启动应用。
大部分代码在灰盒测试和黑盒测试之间共享,因为大部分功能都是如此。两种选择都有各自的优点。使用这两种方法以获得良好的测试金字塔。
-
页面对象
- 可以包含任何代码的函数
- 页面对象元素可以嵌套元素(但是,这可能需要一些不太美观的样板代码)
- 所有(操作/检查)都是完全可扩展的。如果您实现自定义操作或检查,它将自动对黑盒和灰盒测试有效(请参阅
SwipeAction
,所有内置操作实际上都是扩展)。
-
UICollectionView中的每个单元格在测试中都可见(包括屏幕外单元格)
-
可自定义的进程间通信(应用程序与测试之间)
-
测试中可见视图的自定义值
-
网络模拟(通过NSURLSessionProtocol)
-
设置权限(相机/地理位置/通知等)
-
推送通知模拟(限制:仅在活动应用内部!)
-
从测试中打开URL
-
地理位置模拟
-
硬件键盘(定义了非常少的键码,但是可以轻松实现)
-
可自定义,无需分支存储库
-
Swift & Objective-C
-
已测试
- 在3种设备配置上对176个黑盒UI测试进行了测试
- 在3种设备配置上对155个灰盒UI测试进行了测试
- 在4种设备配置上对100个单元测试进行了测试
- SwiftLint +自定义linter
- 所有测试都在向Mixbox的每个拉取请求时执行,通常1个PR等于1个提交。
- 使用5个版本的Xcode(10.0、10.1、10.2.1、10.3、11.0)测试了2个演示
-
可配置的报告(例如:
Tests
项目与用于报告的Web UI的开源报告系统Allure集成;在Avito中使用内部报告解决方案;您可以为报告编写自己的实现)
开发中/尚未开源
- 页面对象的代码生成
- 获取应用中的所有断言错误
- 用于与Springboard一起工作的外观
- 在发布和测试构建之间切换可访问性值
安装
有两种使用Mixbox的方式。
第一种在演示中有描述,这是一个过度简化的版本,基本上您只需使用pod SomePod
即可。
第二种是我们用在Avito的,其样子如下:测试(在Podfile中查看)。
目前文档还不够,因此您可以尝试简单的链接到Mixbox的方法(在演示中查看),但请参考测试中的代码示例。
支持的 iOS/Xcode/Swift 版本
- Xcode 11
- Swift 5
- iOS 10.3, iOS 11.4, iOS 12.1, 中间版本可能工作或不工作,提到的版本已在 CI 上测试
- Cocoapods 1.8.4
- Mac OS 10.14.6
Xcode 9/10 及更旧版本的不再支持。如果您计划在不同环境中使用该项目并遇到问题,请告诉我们。
已知问题
- 在 iOS 11.2 上崩溃(在 iOS 11.3 和 iOS 11.4 上运行良好)
- 设置权限在实体设备上无效(可能还有其他问题,我们在实体设备上未进行测试;基本功能正常运行)
- 未测试设备旋转,我认为我们在此方面存在一些错误
- 未测试 iPad
- 报告中出现俄语(将很快修复)
示例
要查看真实示例,请参阅 Tests
项。这是最新开源示例,如何使用它,但缺乏现实性(它没有显示如何为一个真实的应用编写测试)。
显示基本特征的测试示例
func test() {
// Setting permissions
permissions.camera.set(.allowed)
permissions.photos.set(.notDetermined)
// Functions are useful in page objects and allows
// reusing code, for example, for transitions between states of the app
pageObjects.initial
.authorize(user: testUser)
.goToCvScreen()
// Aliases for simple assertions (you can add your own):
pageObjects.simpleCv.view.assertIsDisplayed()
pageObjects.simpleCv.title.assertHasText("My CV")
// Fully customizable assertions
pageObjects.simpleCv.addressField.assertMatches { element in
element.text != addressFieldInitialText && element.text.isNotEmpty
}
// Network stubbing.
networking.stubbing
.stub(urlPattern: ".*?example.com/api/cv")
.thenReturn(file: "cv.json")
// There is also a monitoring feature, including recording+replaying feature that
// allows to record all network and replay in later, so your tests will not require internet.
// Actions
pageObjects.simpleCV.occupationField.setText("iOS developer")
pageObjects.simpleCV.createCVButton.tap()
}
声明页面对象
public final class MapScreen:
BasePageObjectWithDefaultInitializer,
ScreenWithNavigationBar // protocol extensions are very useful for sharing code
{
// Basic locator
public var mapView: ViewElement {
return element("Map view") { element in
element.id == "GoogleMapView"
}
}
// You can use complex checks
// Note that you can create your own matchers like `element.isFooBar()`
public func pin(coordinates: Coordinates, deltaInMeters: Double = 10) -> ViewElement {
return element("Pin with coordinates \(coordinates)") { element in
element.id == "pin" && element
.customValues["coordinates", Coordinates.self]
.isClose(to: coordinates, deltaInMeters: deltaInMeters)
}
}
}
声明自定义页面对象元素
public final class RatingStarsElement:
BaseElementWithDefaultInitializer,
ElementWithUi
{
public func assertRatingEqualsTo(
_ number: Int,
file: StaticString = #file,
line: UInt = #line)
{
assertMatches(file: file, line: line) { element in
element.customValues["rating"] == number
}
}
}
复制粘贴的代码
该库包含从其他库中复制粘贴的一些代码。有时这是因为我们需要在源中实现条件编译以防止在发布构建中链接代码。
- EarlGrey (每个使用
EarlGrey
的源文件中某处包含EarlGrey
子串)。 许可证在此(Apache)。它是一个可见性检查器,设置可访问性等。原始仓库: https://github.com/google/EarlGrey - AnyCodable。添加了
#if
语句。原始仓库: https://github.com/Flight-School/AnyCodable - SBTUITestTunnel。3.0.6版本之后的应用程序中存在网页视图的bug。随后几年在构建时遇到了不稳定的情况。修复非常原始——复制粘贴一切来避免问题。我们可以深入研究,但由于未来计划使用原生的IPC解决方案,所以没有麻烦。
- CocoaImageHashing。由于向后兼容性问题(ameingast/cocoaimagehashing#13),我们需要修复但不能合并,因为我们不需要向后兼容性,所以使用了复制粘贴的代码。
用于测试
- https://github.com/akveo/eva-icons:用于测试图像相似性代码的各种图标(MIT许可证)。