Swerkin 是一个仪器测试框架,具有 5 个主要目标
扩展现有的开源 iOS 仪器库。
目前,Swerkin 扩展了 KIF - Keep it Functional。但在未来的版本中,可能会扩展 XCUITest 和/或 Earl Grey。
从这些框架构建的测试使用类似 Gherkin 的语法 (Given/When/Then)。
这使得测试的设置、测试正在执行的操作以及测试正在验证的内容易于阅读和理解。
类似 Gherkin 的语法使得开发人员和非开发人员之间的协作更容易构建测试场景
提供预定义步骤定义的目录,以及用 Swift 编写新步骤定义的模式。
创建测试场景的能力应该对开发人员和非开发人员都容易,但应该为开发人员提供熟悉的体验;包括使用自动完成来查找步骤、用 Swift 编写测试场景,以及能够在 XCode 中调试步骤。
能够测试单个屏幕,而无需浏览整个应用程序。
这允许有针对性的测试,并减少了应用程序深层屏幕的测试时间。
能够以易于阅读和简洁的方式测试整个流程。
除了有针对性的测试之外,保留测试整个流程的能力,让您拥有两全其美的优势。为流程测试使用类似 Gherkin 的语法,可以更好地理解测试设置及其验证。
请参阅 贡献文件,了解如何为此框架做出贡献。
Swerkin 在 MIT 许可证下可用。有关更多信息,请参见 LICENSE 文件。
Swerkin 框架有 3 个主要组件:基本测试用例、步骤定义和屏幕对象。
BaseTestCase 是从该框架创建的每个测试类的基础。它提供了每个测试类中的测试可以利用的多个功能。
测试标签:能够向测试类和测试函数添加特定标签,以构建动态测试套件。
TestInfo:用于存储每个测试所需的特定数据的字典。
超时:(可配置)
Preconditions:一个字典,用于保存设置数据,可用于确定如何执行流程和/或端到端测试。
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)
每个屏幕都是Viewable(可查看的)、Assertable(可断言的)、Touchable(可触摸的)、Renderable(可渲染的)和Navigable(可导航的)。每个屏幕包括
PresentableScreen 是一个协议,用于定义应用程序中可呈现屏幕的枚举。
PresentableScreen 是屏幕对象的一个抽象,用于适应分解为单独功能模块的工作空间。所有被测的可呈现屏幕都可以在核心模块中定义,即使屏幕对象在工作空间中的单独功能模块中定义也是如此。
ScreenProvider 是一个类,给定一个 PresentableScreen,可以返回 Screen 类型或 Screen 对象。如果一个工作空间被分解为多个功能模块,则每个模块将为其模块中定义的屏幕对象定义自己的 ScreenProvider。
ScreenPresenter 是一个类,它实现了 ScreenProvider 的注册,以便所有屏幕都可用于测试。
该类还提供了一个方法,给定一个 Presentable Screen,返回一个特定的 Screen Provider 对象,以及一个方法,给定一个 Presentable Screen,返回一个特定的 Screen 对象。
ScreenRenderer 是一个协议,用于从被测系统中创建屏幕。
Swerkin 可以通过 CocoaPods 获得。
要安装它,只需将以下内容添加到您的 Podfile 中
target 'Your Apps' do
...
end
target 'Acceptance Tests' do
pod 'Swerkin'
end
添加后,运行 pod install 完成安装
必须实现三个组件
屏幕类
我们建议创建一个实现 Screen 协议的基本屏幕类,以便可以提供默认值。
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 []
}
}
另一种选择是每个 Screen 对象都应该实现 Screen 协议。
PresentableScreen 枚举
创建一个枚举,其中包含应用程序中要测试的每个屏幕的 case。
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 继承,并覆盖函数 screen 和 typeMarker,以便将 Presentable Screens 转换为它们各自的 Screens。
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
...
}
}
}
Screen Renderer 类
ScreenRenderer 类应该实现 ScreenRender 协议,并为函数 screen 提供一个实现。该函数应该包含三个关键元素
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 继承的 Test Case 类。在 setup 中注册所有的 ScreenProvider,并确保在 tearDown 中重置测试环境,以便每个测试都可以独立运行。
创建 Test Case 类时,也可以覆盖 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 协议的屏幕对象类。对于被测系统中的每个被验证的屏幕,应该有一个屏幕对象类。
每个屏幕对象都应该实现以下内容
创建从应用程序的基本测试用例类继承的测试类。在测试类中,使用类似 Gherkin 的语法,使用步骤定义的目录构建测试用例。
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 下,包括
示例应用程序中每个 ViewController 的屏幕对象示例已添加到 Swerkin-UITests-Examples/Screens 下。
大多数 UI 元素的示例测试用例已添加到 Swerkin-UITests-Examples/Features 下。
以下是使用其可访问性标签验证名字文本字段中的文本的示例测试。
func testVerifyExistenceOfFirstNameTextFieldWithLabel() {
Given.IAmOnScreen(ExamplePresentableScreen.textFieldScreen)
And.IRender(screen: ExamplePresentableScreen.textFieldScreen)
When.IWaitToSeeScreen(ExamplePresentableScreen.textFieldScreen)
Then.IShouldSeeTextField(withLabel: "first name text Field",
withText: "John")
}
要运行示例项目,请克隆 repo,然后首先从 Example 目录运行 pod install
。
选择 CMD-U Swerkin_Example scheme 来执行测试。