Swerkin
Swerkin是一个仪表化测试框架,具有5个主要目标
-
扩展现有的开源iOS仪表化库。
目前,Swerkin扩展的是KIF - Keep it Functional,但在未来的版本中可以扩展XCUITest和/或Earl Grey。
-
从框架构建的测试使用类似Gherkin的语法(Given/When/Then)。
这使得测试的设置、测试采取的动作和验证的内容更容易阅读和理解。
类似Gherkin的语法使得将测试场景作为开发人员和非开发人员之间的合作更为容易。
-
提供一个预设步骤定义的目录和用Swift编写新步骤定义的模式。
创建测试场景的能力应该对开发人员和非开发人员都比较简单,但应该为开发者提供熟悉体验;这包括使用智能感知来查找步骤、在Swift中编写测试场景以及在XCode中调试步骤。
-
能够测试单个屏幕,而无需在应用程序中导航。
这使得可以进行有针对性的测试,并减少了可能出现在应用程序深处的屏幕的测试时间。
-
能够以易于阅读和简洁的方式测试整个流程。
保留测试整个流程的能力,同时能够针对测试进行测试,这样就兼顾了两种方法。利用类似Gherkin的语法进行流程测试,可以更好地理解测试设置及其验证。
如何贡献
请参阅贡献文件,了解如何参与此框架的开发。
许可证
Swerkin遵循MIT许可证。有关更多信息,请参阅LICENSE文件。
Swerkin 功能
Swerkin框架由3个主要组件组成:基本测试用例、步骤定义和屏幕对象。
BaseTestCase
BaseTestCase是该框架中每个测试类的基础。它提供了一些功能,可以在每个测试类内部进行测试。
-
测试标签:能够向测试类和测试函数添加特定标签,以构建动态测试套件。
-
测试信息:存储每个测试所需特定数据的字典。
-
超时:(可配置)
- testTimeout:整个测试的超时时间,在失败前(默认:60秒)
- waitingTimeout:等待条件为真时的超时时间(默认:2秒)
- validationTimeout:在验证失败前的超时时间(默认:2秒)
-
前置条件:一个字典,用于存储设置数据,可以用来确定如何执行流程和/或端到端测试。
-
CurrentScreen:测试开始时可以展示的屏幕。
-
ScreenPresenter:一个特定的类,可以注册所有要测试的屏幕对象,并在需要时返回一个特定的屏幕。
-
Swerkin 对象:包括 Given / When / Then / And,提供类似 Gherkin 的体验,如果需要其他 Gherkin 语法可以进一步扩展。
步骤定义
Swerkin 步骤定义分为三种类型
- 设置
- 操作
- 断言
设置
设置步骤定义用于设置和渲染测试下的屏幕(们)。
//Sets the current screen in the test case to a given presentable screen
func IAmOnScreen(screen: PresentableScreen)
//Render a given presentable screen from the system under test
func IRender(screen: PresentableScreen)
//Navigate from one screen to another via a set of step definitions
func INavigate(fromScreen: PresentableScreen,
toScreen:PresentableScreen)
操作
操作步骤定义用于使用元素的可访问属性(ID、标签、特性等)与之交互。
//Touch button with given accessibility identifier
public func ITouchButton(_ buttonId: String)
//Touch button with given accessibility label
public func ITouchButton(withLabel buttonLabel: String)
//Enter text into a text field with a given accessibility identifier
public func IEnterIntoTextField(_ id: String, text: String)
断言
断言步骤定义用于使用元素的可访问属性(ID、标签、特性等)进行验证。
//Verifies a UIButton exists with the given accessibility identifier
public func IShouldSeeButton(_ buttonId: String)
//Verifies a UIButton exists with the given accessibility label
public func IShouldSeeButton(withLabel buttonLabel: String)
//Verifies a UITextField with a given accessibility identifier contains specific text
public func IShouldSeeTextField(_ textFieldId: String,
withText text: String)
屏幕
屏幕对象
每个屏幕是 可查看的、可断言的、可触摸的、可渲染的 和 可导航的。每个屏幕包括
- 当前测试用例的引用
- 用于识别屏幕的唯一特征
- 屏幕名称
- 渲染屏幕的方法
- 在流程测试期间辅助导航到屏幕的入口点列表
PresentableScreen & ScreenProvider
PresentableScreen 是一个协议,用于定义应用中可呈现屏幕的枚举。
PresentableScreen 是从屏幕对象抽象出来的,以适应分解为单独功能模块的工作空间。即使是屏幕对象定义在工作空间内部的单独功能模块中,所有可呈现的测试屏幕也可以在核心模块中定义。
ScreenProvider 是一个类,给定一个 PresentableScreen 可以返回屏幕类型或屏幕对象。如果工作空间分解为多个功能模块,则每个模块将为模块内定义的屏幕对象定义自己的 ScreenProvider。
ScreenPresenter
ScreenPresenter 是一个类,用于实现 ScreenProviders 的注册,使所有屏幕都可用于测试。
该类还提供了一个方法,用于根据可展示的屏幕返回特定屏幕提供者对象,以及一个方法,用于根据可展示的屏幕返回特定屏幕对象。
ScreenRenderer
ScreenRenderer 是一个创建测试中系统屏幕的协议。
安装
Swerkin 通过 CocoaPods 提供。
要安装它,只需将以下内容添加到您的 Podfile
target 'Your Apps' do
...
end
target 'Acceptance Tests' do
pod 'Swerkin'
end
添加后,运行 pod install 完成安装
实现
必须实现三个组件
- 测试核心类
- 屏幕对象类
- 测试类(也称为功能或规范)
测试核心类
屏幕类
我们建议创建一个实现屏幕协议的基础屏幕类,以便提供默认值。
class ExampleBaseScreen: Screen {
final var test: BaseTestCase
final let renderer: ScreenRenderer = ExampleScreenRenderer()
public required init(testCase: BaseTestCase) {
self.test = testCase
}
final var testName: String { return test.name }
var trait: String { return "" }
var name: String { return "" }
func create() -> UIViewController { UIViewController() }
func renderScreen() {}
func entryPathSegments() -> [PathSegment] {
return []
}
}
另一种选择是每个屏幕对象都应该实现屏幕协议。
PresentableScreen 枚举
为应用中要测试的每个屏幕创建一个枚举案例。
public enum ExamplePresentableScreen: String, PresentableScreen {
case buttonScreen
case dropDownScreen
case endToEndScreen
case homeScreen
case swipeScreen
case tappableScreen
case textFieldScreen
case waitToSeeScreen
case tableViewScreen
public var rawValue: String {
get {
return String(describing: self).capitalized
}
}
}
ScreenProvider 类
ScreenProvider 类应该继承自 ScreenProvider,并覆盖用于转换的 Presentable Screens 对应的屏幕的 screen 和 typeMarker 函数。
public class ExampleScreenProvider: ScreenProvider<ExamplePresentableScreen> {
public var testReference: BaseTestCase! = nil
public required init(testCase: BaseTestCase?) {
self.testReference = testCase
}
public override func screen(for screen: ExamplePresentableScreen) -> Screen? {
switch screen {
case .buttonScreen:
return ButtonScreen(testCase: self.testReference)
case .dropDownScreen:
return DropdownScreen(testCase: testReference)
...
}
}
public override func typeMarker(for screen: ExamplePresentableScreen) -> Screen.Type? {
switch screen {
case .buttonScreen:
return ButtonScreen.self
case .dropDownScreen:
return DropdownScreen.self
...
}
}
}
屏幕渲染类
ScreenRenderer 类应该实现 ScreenRender 协议,并为函数 screen 提供实现。该函数应该具有三个关键元素:
- 创建要测试的屏幕(可能是 ViewController)
- 将正在测试的屏幕添加到导航堆栈的顶部
- 验证屏幕上(特性)出现的唯一元素符合预期
class ExampleScreenRenderer: ScreenRenderer {
func screen(_ screenObject: Screen, didRenderWithAuth isAuth: Bool) {
guard let screenObject = screenObject as? ExampleBaseScreen else { return }
if isAuth {
//Add code that is special to your app when the user is authenticated
}
//Navigation code to render the ViewController and add it to the stack to navigate directly to it
if let navigationController = UIApplication.shared.topNavigationController() {
navigationController.pushViewController(screenObject.create(), animated: false)
}
screenObject.viewTester.waitForAnimationsToFinish()
screenObject.waitForElement(withIdentifier: screenObject.trait)
}
}
测试用例类
为您的测试创建一个继承自 BaseTestCase 的 TestCase 类。在设置期间注册所有 ScreenProviders,并确保在 tearDown 中重置测试环境,以确保每个测试可以独立运行。
在创建 TestCase 类时,也可以覆盖 BaseTestCase 的默认值。
open class ExampleTestCase: BaseTestCase {
open override func setUp() {
super.setUp()
self.screenPresenter.registerScreenProvider(ExampleScreenProvider(testCase: self), for: ExamplePresentableScreen.self)
}
open override func tearDown() {
resetNavigation {
self.navigateHome()
self.waitForAnimationsToFinish()
}
super.tearDown()
}
...
}
屏幕对象类
创建继承自您的基屏幕对象或实现 Screen 协议的屏幕对象类。应为系统测试中验证的每个屏幕创建一个屏幕对象类。
每个屏幕对象应实现以下内容
- var trait
- var name
- func create()
- func entryPathSegments()
- func renderScreen()
- enum View: Accessibility
测试类(即功能/规格)
创建继承自您的应用程序的基测试用例类的测试类。在测试类中,使用 Gherkin-like 语法构建测试用例,使用步骤定义目录。
class Dropdown: ExampleTestCase {
private let textField = DropdownScreen.View.textField.accessibilityIdentifier
func testVerifySingleItem() {
Given.IAmOnScreen(ExamplePresentableScreen.dropDownScreen)
And.IRender(screen: ExamplePresentableScreen.dropDownScreen)
When.ISetDropDown(textField, toValue: "Banana")
Then.IShouldSeeTextField(textField, withText: "Banana")
}
func testVerifyWithLabelItem() {
Given.IAmOnScreen(ExamplePresentableScreen.dropDownScreen)
And.IRender(screen: ExamplePresentableScreen.dropDownScreen)
When.ISetDropDown(withLabel: "textField", toValue: "Orange")
Then.IShouldSeeTextField(textField, withText: "Orange")
}
...
}
示例应用程序
创建了一个示例应用程序,以展示如何实现框架。如前所述,实现 Swerkin 测试应用程序框架需要实施三个组件:
- 测试核心类
- 屏幕对象类
- 测试用例
测试核心类
在Swerkin-UITests-Examples/TestCore目录下添加了示例测试核心类,包括
- ExampleBaseScreen
- ExamplePresentableScreen
- ExampleScreenRenderer
- ExampleScreenProviders
- ExampleTestCase
屏幕对象类
在Swerkin-UITests-Examples/Screens目录下添加了示例应用中每个ViewController的屏幕对象示例。
测试用例
在Swerkin-UITests-Examples/Features目录下添加了大部分UI元素的示例测试用例。
这里是一个使用其无障碍标签验证第一个名字文本框内文本的示例测试。
func testVerifyExistenceOfFirstNameTextFieldWithLabel() {
Given.IAmOnScreen(ExamplePresentableScreen.textFieldScreen)
And.IRender(screen: ExamplePresentableScreen.textFieldScreen)
When.IWaitToSeeScreen(ExamplePresentableScreen.textFieldScreen)
Then.IShouldSeeTextField(withLabel: "first name text Field",
withText: "John")
}
如何运行示例应用及UI测试
要运行示例项目,首先克隆存储库,然后从Example目录运行pod install
。
选择CMD-U Swerkin_Example方案以执行测试。