Mock 'N Stub
完成 Swift 协议和类的 Mocking 和 Stubbing。
示例
要查看示例项目,请在您的终端中运行以下命令
pod try MockNStub
设置
只需将
import MockNStub
添加到您需要创建模拟和存根的文件中。
所有模拟都是存根
所有创建的模拟都遵循 Mocking
协议,并且因为 Mocking
遵循 Stubbing
协议,所以所有创建的模拟也可以自动用作存根。
当您发现确实需要显式存根支持 Mocking
时,您需要做的就是将其从 Stubbing
改为 Mocking
。
类和协议的模拟/存根共享完全相同的接口
MockNStub 中的实现完全基于协议。这允许类和协议模拟(以及存根)的接口完全相同。所有显式存根都遵循 Stubbing
,所有模拟都遵循 Mocking
。无需从此库中继承具体的类型。
存根
创建存根
使用函数名
class UITableViewDataSourceStub: Stubbing, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return didCallFunction(withArguments: tableView, section)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
return didCallFunction(withArguments: tableView, indexPath)
}
}
注意
- 无需手动提供函数名。
向存根添加返回值
可以添加任意数量的返回值,如果在同一签名下提供了它们,则返回最后一次提供的值。
使用函数名
考虑
let stub = UITableViewDataSourceStub()
您可以添加存根值如下
stub.given("tableView(_:numberOfRowsInSection:)", willReturn: 0)
stub.given("tableView(_:cellForRowAt:)", willReturn: UITableViewCell())
或者,当需要更具体时,如下所示
stub.given("tableView(_:numberOfRowsInSection:)"), withArgumentsThatMatch: ArgumentMatcher(matcher: { (args: (UITableView, Int)) -> Bool in
return args.0 === expectedTableView && args.1 == 2
}), willReturn: 42)
注意
- 如果参数类型不正确,则参数匹配器不会匹配。
模拟
创建模拟
使用函数名
class UITableViewDataSourceMock: NSObject, Mocking, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return didCallFunction(withArguments: tableView, section)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
return didCallFunction(withArguments: tableView, indexPath)
}
}
创建期望
使用函数名
let mock = UITableViewDataSourceMock()
您可以添加期望如下
mock.expect(callToFunction: "tableView(_:cellForRowAt:)")
mock.expect(callToFunction: "tableView(_:numberOfRowsInSection:)")
或者,当需要更具体时,如下所示
mock.expect(callToFunction: "tableView(_:numberOfRowsInSection:)", withArgumentsThatMatch: ArgumentMatcher(matcher: { (args: (UITableView, Int)) -> Bool in
return args.0 === tableView && args.1 == 42
}))
也可以期望调用精确次数
mock.expect(.exactly(amount: 42), callsToFunction: "tableView(_:cellForRowAt:)")
或
mock.expect(.exactly(amount: 42), callsToFunction: "tableView(_:numberOfRowsInSection:)", withArgumentsThatMatch: ArgumentMatcher(matcher: { (args: (UITableView, Int)) -> Bool in
return args.0 === tableView && args.1 == 42
}))
验证
无论方法的识别方式如何
mock.verify()
注意
- 如果有一个或多个期望未被满足,这将导致 XCT 失败。
属性
模拟和存根属性与预期一致。
使用函数名
var title: String {
get {
return didCallFunction()
}
set {
didCallFunction(withArguments: newValue)
}
}
注意
- 任何属性的这种 get set 模式都是相同的。
didCall() 的默认返回值
在 Mocking
和 Stubbing
协议中都有很多针对 didCall
方法的实现。由于 Swift 对类型推断的支持,在编译时将使用正确的方法。例如,当调用 return didCallFunction()
时,将使用非空的 didCallFunction()
实现人员。更有趣的是,当在返回符合 ProvidingDefaultStubValue
的值的函数中调用 return didCallFunction()
时,不需要解开 didCallFunction
的结果,因为默认值已知(并通过默认协议实现提供)。注意:这些默认存根值只有在没有通过 given...
方法提供其他值时才会提供。
不要过于担心以上解释,简而言之:您的 IDE 将始终为您提供可用的最佳选择。
当需要返回不遵守 ProvidingDefaultStubValue
的类型时。编译器不会建议(并允许)返回非可选值的 didCall..
版本。在这种情况下,你可以做以下三件事:
- 使该类型遵守
ProvidingDefaultStubValue
- 如果你是为 Apple 库中的类型这样做,对这个仓库中包含此扩展的 pull request 会非常感激。
- 手动提供默认值,以防提供 nil:
return didCallFunction() ?? MyType()
- 强制解包
didCall
提供的返回值
目前遵守 ProvidingDefaultStubValue
的类型
- Swift 标准库中的大多数类型
- UIKit 中最常用的类型
- CoreGraphics 中最常用的类型
- 所有继承自 NSObject 的类型
- 免责声明;这些子类确实需要遵守 Liskov 替换原则,简单来说:不要在它们的
init()
中有fatalError()
或类似的内容
- 免责声明;这些子类确实需要遵守 Liskov 替换原则,简单来说:不要在它们的
这里 是所有目前遵守 ProvidingDefaultStubValue
的类型的概览
定义函数 ID
通过让你的模拟和代理遵守 DefiningFunctionID
来减少由拼写错误引起的错误的一种方式
遵守 DefiningFunctionID
的方式如下
extension UITableViewStub : DefiningFunctionID {
typealias FunctionID = FuncID
enum FuncID: String {
case numberOfRows
case cellForRowAt
}
}
注意:上面的例子是为代理做的,但同样适用于模拟
遵守 DefiningFunctionID
将解锁下述范围的模拟和代理方法
didCallFunction(withID: .numberOfRows)
mock.expect(callToFunctionWithID: .numberOfRows)
还有更多内容要来..
计划的功能
可以在 路线图 中查看。
还有什么遗漏的吗?
创建一个 功能请求,它很可能会被选中。
安装
MockNStub 通过 CocoaPods 提供。要安装它,只需将以下行添加到你的 Podfile 中
pod 'MockNStub'
许可
MockNStub 在 MIT 许可下提供。有关更多信息,请参阅 LICENSE 文件。