概览
强大的 iOS E2E UI 测试框架。
目前它在 Avito 上使用,我们拥有 700+ 的 E2E UI 测试,其中约 90% 是绿色的,并帮助我们减少了 2 年的手动测试。我们在合并请求(PR)中运行大约 25%,并且我们正努力在合并请求上执行 100% 的测试,部分是通过从黑盒 E2E 转换到灰盒测试实现的。我们在 3 个平台上运行这些测试,耗时约 40 分钟(测试总时长为 30+ 小时),因为我们使用的是 Emcee,一个在多台机器上运行测试的测试运行器(请注意,Mixbox 不需要 Emcee)。我们也正在编写灰盒测试(其中我们模拟类、网络等),但我们刚刚开始。
如果您想在自己的公司使用它,请给我们提交一个问题。我们正在使其可供社区使用,但这不是我们的主要目标。
特点
-
操作和检查(显然)
-
像素级可见性检查
-
所有内容都有轮询
-
完全自动滚动
-
黑盒和灰盒测试!
- 黑盒测试在单独的应用程序中运行,能够启动应用程序。您可以使用这种类型的测试测试更多应用程序功能。您可以使用模拟,但这将需要您编写更多代码(使用启动参数或实现进程间通信)。
- 灰盒测试是在应用程序内部运行的(如 EarlGrey),但测试与黑盒测试完全兼容,因此您可以共享您的测试模型、页面对象、测试助手等。您可以轻松模拟任何内容,因为测试是在相同进程中执行的。您不能在没有使用模拟的情况下测试启动您的应用。
大多数代码在灰盒测试和黑盒测试之间共享,因为大多数功能。两种选项都有自己的优点。同时使用这两种方法,以获得良好的 测试金字塔。
-
页面对象
- 可以包含任何代码的函数
- 页面对象元素可以嵌套元素(然而,这需要一些不太美观的样板代码)
- 所有(操作/检查)都完全可以扩展。如果您实现了自定义操作或检查,它将自动对黑盒和灰盒测试有效(参见
SwipeAction
,所有内置操作都是真正的扩展)。
-
UICollectionView中每个单元都在测试中可见(包括屏幕外的单元格)
-
自定义应用程序与测试之间的进程间通信
-
测试中可见的视图的自定义值
-
网络模拟(通过 NSURLSessionProtocol)
-
设置权限(相机/地理位置/通知等)
-
推送通知模拟(限制:仅限于活动应用中!)
-
从测试中打开 URL
-
地理位置模拟
-
硬件键盘(定义了很少键码,但可以轻松实现)
-
无需分叉存储库即可自定义
-
Swift & Objective-C
-
经过测试
- 在 3 种设备配置上的 176 个黑盒 UI 测试
- 在 3 种设备配置上的 155 个灰盒 UI 测试
- 在 4 种设备配置上的 100 个单元测试
- SwiftLint +自定义分析器
- 所有测试都在 Mixbox 的每个 pull request 上执行,并且通常 1 PR 等于 1 次提交。
- 使用 5 个版本的 Xcode(10.0、10.1、10.2.1、10.3、11.0)对 2 个演示进行了测试。
-
可配置的报告(例如:
Tests
项目的集成功能是 Allure,一个具有 Web UI 的开源报告系统;在 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 上运行良好)
- 在物理设备上设置权限不工作(也许还有其他,我们没有在物理设备上测试;基本事情都运行良好)
- 未测试设备旋转,我认为我们在这方面有错误
- 未测试 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)在此](/Docs/EarlGreyLicense/LICENSE)。它是可见性检查器,设置可访问性等。原始仓库:https://github.com/google/EarlGrey - 添加了
#if
语句。原始仓库:https://github.com/Flight-School/AnyCodable - SBTUITestTunnel。与版本大于3.0.6的web视图存在一个bug。然后几年后我们在构建时遇到了不可靠的问题。修复非常原始——将一切复制粘贴以避免问题。我们可以深入研究,但是有一个计划未来使用本机解决方案来进行IPC,所以我们没有麻烦。原始仓库:https://github.com/Subito-it/SBTUITestTunnel
- CocoaImageHashing。我们需要一个不能合并的修复,因为与向后兼容性相关(《ameingast/cocoaimagehashing#13》),我们不需要向后兼容性,因此我们使用了复制粘贴的代码。原始仓库:https://github.com/ameingast/cocoaimagehashing
用于测试
- 收录于[eva-icons](https://github.com/akveo/eva-icons):用于测试图像相似性代码的各个图标(MIT许可证)。