概览
强大的iOS端到端UI测试框架。
目前它被用于Avito,我们拥有700+个端到端UI测试用例,其中约90%为绿色,帮助我们减少了两年来的手动测试工作量。我们大约有25%的测试是在PR上运行的,我们正在努力实现所有测试在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 +自定义lint
- 所有测试都在每个向Mixbox提交的拉取请求时执行,通常一个PR等于一个提交。
- 使用5个版本的Xcode(10.0、10.1、10.2.1、10.3、11.0)测试了两个示例。
-
可配置报告(例如:
Tests
项目与具有Web UI的开源报告系统Allure集成,而在Avito,我们使用自家的报告解决方案;您可以编写自己的实现)
开发中/尚未开源
- 页面对象的代码生成
- 从应用程序中获取所有断言失败
- 用于处理春季板的外观
- 在发布和测试构建之间切换可访问性值
安装
使用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 上运行正常)
- 在实体设备上设置权限不起作用(可能还有其他一些,我们没有在实体设备上测试;基本功能正常工作)
- 设备旋转没有进行测试,我认为我们在这方面有 bugs
- 未测试 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的web视图上存在一个bug。然后几年后,在构建时遇到了不确定(flakiness)的问题。修复非常原始——复制粘贴所有内容以避免问题。我们可以深入挖掘,但我们有计划未来使用原生的IPC解决方案,因此没有麻烦。《strong>原始仓库:https://github.com/Subito-it/SBTUITestTunnel
- CocoaImageHashing。我们需要一个不能合并的修复,因为向后兼容性(ameingast/cocoaimagehashing#13),我们不需要向后兼容性,因此使用复制粘贴的代码。《strong>原始仓库:https://github.com/ameingast/cocoaimagehashing
用于测试
- https://github.com/akveo/eva-icons:用于测试图像相似性代码的各种图标(MIT许可)。