测试已测试 | ✓ |
语言编程语言 | SwiftSwift |
许可证 | MIT |
发布日期上次发布日期 | 2017年11月 |
SwiftSwift 版本 | 4.0 |
SPM支持 SPM | ✗ |
由 Ondrej Rafaj 维护。
这是一个库,可以帮助您在 iOS 应用界面测试时编写更少的代码。
在您添加 SpecTools 框架后,可以通过 spec 属性获得一组选项,用于大多数 UI 元素。
这些包括
pushViewController
或 popViewController
方法这里有许多更多附加功能的空间,因此请随时提出功能请求。
以下是一个示例实现
import Foundation
import UIKit
import Quick
import Nimble
import SpecTools
@testable import SpecToolsExample
class ViewControllerSpec: QuickSpec {
override func spec() {
let subject = ViewController()
describe("basic view controller") {
beforeEach {
// Simulate view controller being presented to the screen
subject.spec.prepare.simulatePresentViewController()
// Reset the view to specific size
subject.spec.prepare.set(viewSize: .iPhone6Plus)
}
it("has a visible label1") {
// Get your first label
let element = subject.view.spec.find.first(labelWithText: "My first label")
// Check if the label is truly visible and print out the entire view structure that is being checked
expect(element?.spec.check.isVisible(visualize: .text)).to(beTrue())
}
it("has a visible scrollView") {
// Get a scroll view
let element = subject.view.spec.find.firstScrollView()
// Check visibility
expect(element?.spec.check.isVisible()).to(beTrue())
}
it("has a visible label2", closure: {
// Get a label that contains "second label" and print how we get to it in the console including any text on visible elements
let element = subject.view.spec.find.first(labelWithText: "My second label", exactMatch: false, visualize: .text)
// Check if the label is visible on subjects view and print all frames we encounter on the way
expect(element?.spec.check.isVisible(on: subject.view, visualize: .frames)).to(beTrue())
})
describe("when we tap on button1") {
beforeEach {
// Simulate button tap
button1.spec.action.tap()
}
it("should have pushed new view controller") {
// Check we have new view controller in the navigation stack
expect(subject.navigationController?.spec.check.contains(viewControllerClass: TableViewController.self)).to(beTrue())
}
}
}
}
}
要运行示例项目
pod install
在线文档应始终反映 master
分支上的最新代码。
此库可以完全独立运行。它不需要快速和敏捷,尽管我们强烈建议您尝试这些库!
对于 Swift 4 支持请参阅 swift4
分支!
某些方法有可用的调试机制。最常见的一个是 visualize: VisualizationType
参数,它允许您将调试数据(通常是 SpecTools 递归通过元素的树)打印到控制台。
有关可用的可视化选项,请参阅文档:VisualizationType
以下部分应包含所有可用的方法
模拟 UIButton 上的点击
button1.spec.action.tap()
// or
button1.spec.action.tap(event: .touchUpInside)
模拟带有 UITapGestureRecognizer 的视图上的点击
view.spec.action.triggerTap()
// or
view.spec.action.triggerTap(taps: 3, touches: 2)
在任何 UIGestureRecognizer 上执行操作
recognizer.spec.action.execute()
从任何 UIGestureRecognizer 获取目标数组
[(target: AnyObject, action: Selector)]
recognizer.spec.action.getTargetInfo()
视图是否真正显示在屏幕上?检查元素(及其所有父视图)是否有父视图、是否有透明度、未隐藏并且有一个有效的屏幕帧。
view.spec.check.isVisible() // Example 1
// or
view.spec.check.isVisible(on: viewController.view, visualize: .all) // Example 2
示例 1)忽略最后一个没有父视图的视图,例如,期望它为视图控制器的视图
示例 2)将可见性与另一个视图进行比较,还可以将整个递归视图结构打印到控制台
检查所有通过 UITableViewDataSource 获取的 UITableViewCell 是否评估正确
// Create an enumerate closure
let doesFitClosure: (UITableViewCell)->Bool = { (cell) -> Bool in
guard let cell = cell as? CustomTableViewCell else {
return false
}
if cell.customLabel.text?.characters.count == 0 {
return false
}
return true
}
// Test if all cells generated by the data source are ok using your closure
let ok = subject.tableView.spec.check.allCells(fit: doesFitClosure)
您还可以要求一个数组,该数组不满足条件
let indexPaths: [IndexPath] = subject.tableView.spec.check.allCells(thatDontFit: doesFitClosure)
检查所有通过 UICollectionView 数据源获取的 UICollectionViewCell 是否评估正确
// Create an enumerate closure
let doesFitClosure: (UICollectionViewCell)->Bool = { (cell) -> Bool in
guard let cell = cell as? CustomCollectionViewCell else {
return false
}
if cell.customLabel.text?.characters.count == 0 {
return false
}
return true
}
// Test if all cells generated by the data source are ok using your closure
let ok = subject.collectionView.spec.check.allCells(fit: doesFitClosure)
您还可以要求一个数组,该数组不满足条件
let indexPaths: [IndexPath] = subject.collectionView.spec.check.allCells(thatDontFit: doesFitClosure)
查找导航堆栈中的特定视图控制器
let ok: Bool = viewController.spec.check.has(siblingInNavigationStack: anotherViewController)
检查视图控制器是否有子视图控制器
let ok: Bool = viewController.spec.check.has(childViewController: childViewController)
检查视图控制器在导航堆栈中是否有特定类类型
let ok: Bool = viewController.spec.check.contains(siblingClassInNavigationStack: AnotherViewController.self)
检查视图控制器是否有子视图控制器的特定类类型
let ok: Bool = viewController.spec.check.contains(childViewControllerClass: ChildViewController.self)
检查导航视图控制器是否包含某些视图控制器
let ok: Bool = navigationController.spec.check.has(viewController: vc)
检查导航视图控制器是否包含某些类型的视图控制器
let ok: Bool = viewController.spec.check.contains(viewControllerClass: MyCustomViewController.self)
在视图上查找特定类型的手势识别器(泛型方法)
let recognizers: [UISwipeGestureRecognizer] = view.spec.find.all(gestureRecognizersOfType: UISwipeGestureRecognizer.self)
在特定视图中查找第一个包含或匹配所需字符串的标签(UILabel)。要按仅部分字符串/内容搜索元素,请使用 exactMatch: Bool
标志。要可视化元素路径,请使用 visualize: VisualizationType
。请参阅 调试 部分,获取更多详细信息。
let element = view.spec.find.first(labelWithText: "My first label")
// or
let element = view.spec.find.first(labelWithText: "first", exactMatch: false, visualize: .text)
在特定视图中查找第一个包含或匹配所需字符串的文本字段(UITextField)。要按仅部分字符串/内容搜索元素,请使用 exactMatch: Bool
标志。要可视化元素路径,请使用 visualize: VisualizationType
。请参阅 调试 部分,获取更多详细信息。
let element = view.spec.find.first(textFieldWithText: "My first text field")
// or
let element = view.spec.find.first(textFieldWithText: "first", exactMatch: false, visualize: .text)
在特定视图中查找第一个包含或匹配所需字符串的搜索栏(UISearchBar)。要按仅部分字符串/内容搜索元素,请使用 exactMatch: Bool
标志。要可视化元素路径,请使用 visualize: VisualizationType
。请参阅 调试 部分,获取更多详细信息。
let element = view.spec.find.first(searchBarWithText: "My first search bar")
// or
let element = view.spec.find.first(searchBarWithText: "first", exactMatch: false, visualize: .text)
在特定视图中查找第一个包含或匹配所需字符串的文本视图(UITextView)。要按仅部分字符串/内容搜索元素,请使用 exactMatch: Bool
标志。要可视化元素路径,请使用 visualize: VisualizationType
。请参阅 调试 部分,获取更多详细信息。
let element = view.spec.find.first(textViewWithText: "My first text view")
// or
let element = view.spec.find.first(textViewWithText: "first", exactMatch: false, visualize: .text)
在特定视图中查找第一个包含或匹配所需字符串的表格视图单元格(UITableViewCell)。要按仅部分字符串/内容搜索元素,请使用 exactMatch: Bool
标志。要可视化元素路径,请使用 visualize: VisualizationType
。请参阅 调试 部分,获取更多详细信息。
let element = view.spec.find.first(tableViewCellWithText: "My first cell")
// or
let element = view.spec.find.first(tableViewCellWithText: "first", exactMatch: false, visualize: .text)
在特定视图中查找第一个包含或匹配所需字符串的按钮(UIButton)。要按仅部分字符串/内容搜索元素,请使用 exactMatch: Bool
标志。要可视化元素路径,请使用 visualize: VisualizationType
。请参阅 调试 部分,获取更多详细信息。
let element = view.spec.find.first(buttonWithText: "My first button")
// or
let element = view.spec.find.first(buttonWithText: "first", exactMatch: false, visualize: .text)
在特定视图中查找第一个包含或匹配所需字符串的表格视图头部或尾部视图(UITableViewHeaderFooterView)。要按仅部分字符串/内容搜索元素,请使用 exactMatch: Bool
标志。要可视化元素路径,请使用 visualize: VisualizationType
。请参阅 调试 部分,获取更多详细信息。
let element = view.spec.find.first(tableSectionHeaderFooterViewWithText: "My first table header or footer view")
// or
let element = view.spec.find.first(tableSectionHeaderFooterViewWithText: "first", exactMatch: false, visualize: .text)
在特定的视图中查找第一个UITableView的头或尾(不是分区头或尾),其中包含或匹配所需的字符串。您正在搜索的视图可能包含多个UITableView。为了通过仅部分字符串/内容来搜索元素,请使用exactMatch: Bool
标志。要可视化元素的路径,请使用visualize: VisualizationType
。请参阅调试部分以获取更多详细信息。
let element = view.spec.find.first(tableHeaderFooterViewWithText: "My first header view")
// or
let element = view.spec.find.first(tableHeaderFooterViewWithText: "first", exactMatch: false, visualize: .text)
在特定的父视图中查找第一个视图(任何UIView),其中包含或匹配所需的字符串。为了仅通过部分字符串/内容搜索元素,请使用exactMatch: Bool
标志。要可视化元素的路径,请使用visualize: VisualizationType
。请参阅调试部分以获取更多详细信息。
let element = view.spec.find.first(elementWithText: "My first view with some text on a label deep inside")
// or
let element = view.spec.find.first(elementWithText: "deep", exactMatch: false, visualize: .text)
在特定的视图中查找第一个元素(任何UIView子类(Element)),其中包含或匹配所需的字符串。为了仅通过部分字符串/内容搜索元素,请使用exactMatch: Bool
标志。要可视化元素的路径,请使用visualize: VisualizationType
。请参阅调试部分以获取更多详细信息。
let element = view.spec.find.first(elementOfType: MyCustomView.self, withText: "My first custom view with some text on a label deep inside")
// or
let element = view.spec.find.first(elementOfType: UILabel.self, withText: "custom", exactMatch: false, visualize: .text)
在特定的视图中查找第一个tableView(UITableView)。要可视化元素的路径,请使用visualize: VisualizationType
。请参阅调试部分以获取更多详细信息。
let element = view.spec.find.firstTableView()
// or
let element = view.spec.find.firstTableView(visualize: .frames)
在特定的视图中查找第一个collectionView(UICollectionView)。要可视化元素的路径,请使用visualize: VisualizationType
。请参阅调试部分以获取更多详细信息。
let element = view.spec.find.firstCollectionView()
// or
let element = view.spec.find.firstCollectionView(visualize: .frames)
在特定的视图中查找第一个scrollView(UIScrollView)。要可视化元素的路径,请使用visualize: VisualizationType
。请参阅调试部分以获取更多详细信息。
let element = view.spec.find.firstScrollView()
// or
let element = view.spec.find.firstScrollView(visualize: .frames)
在特定的视图中查找第一个table header或 footer视图(UITableViewHeaderFooterView)。要可视化元素的路径,请使用visualize: VisualizationType
。请参阅调试部分以获取更多详细信息。
let element = view.spec.find.firstTableHeaderFooterView()
// or
let element = view.spec.find.firstTableHeaderFooterView(visualize: .frames)
在特定的视图中查找第一个元素(通用方法)。要可视化元素的路径,请使用visualize: VisualizationType
。请参阅调试部分以获取更多详细信息。
let element = view.spec.find.first(elementOfType: MyCustomView.self)
// or
let element = view.spec.find.first(elementOfType: UIButton.self, visualize: .frames)
在特定的视图中查找特定类型的所有元素。要可视化元素的路径,请使用visualize: VisualizationType
。请参阅调试部分以获取更多详细信息。
let element = view.spec.find.all(elementsOfType: UITextField.self)
// or
let element = view.spec.find.all(elementsOfType: MyCustomView.self, visualize: .frames)
以重要性顺序从特定视图中获取文本(首先检查文本字段上的第一个文本,如果没有文本则查找占位符)。您可以使用preferablyMatching: String
指定要查找的文本。如果没有找到匹配的字符串,则方法将回退到其原始的优先级顺序。如果没有直接可用的文本属性,则方法返回nil。
此方法仅适用于UILabel
、UITextField
、UISearchBar
或UITextView
let element = view.spec.find.anyText()
// or
let element = view.spec.find.anyText(preferablyMatching: "Welcome to my fun app!")
将触摸视图控制器的视图以获取loadView和viewDidLoad的调用,然后手动调用带有禁用动画的viewWillAppear和viewDidAppear
viewController.spec.prepare.simulatePresentViewController()
在运行时为视图控制器的视图设置新的、特定的大小
viewController.spec.prepare.set(viewSize: CGSize(width: 375.0, height: 1500))
在您的视图控制器的视图中设置期望设备的屏幕大小,您可以指定自定义高度。自定义高度可能对于有scrollviews的情况很有用
viewController.spec.prepare.set(viewSize: .iPhone6Plus)
// or
let customHeight: CGFloat = 5000.0
viewController.spec.prepare.set(viewSize: .iPhone6Plus, height: customHeight)
为视图控制器提供导航控制器
viewController.spec.prepare.assignNavigationController()
// or
viewController.spec.prepare.assignNavigationController(ofClass: CustomNavigationViewController.self)
为视图控制器提供模拟导航控制器,这主要允许进行推/弹出功能的测试
viewController.spec.prepare.assignMockNavigationController()
Ondrej Rafaj,[email protected]
SpecTools仅在MIT许可下可用。有关更多信息,请参阅LICENSE文件。