SwiftMock
SwiftMock 是Swift 2.0的mocking/stubbing框架的第一个尝试。它处于开发初期,但可能可用。
我将其公开发布是为了获取一些关于API的反馈,正如在您的测试中使用的那样。
注意:这是一个Swift 2.0项目,因此需要Xcode 7.0才能构建。
限制
- 创建mock需要一些模板代码。请参阅
MockExampleCollaborator
并查看下面的 用法 部分。 - (目前)不支持stubbing,显式拒绝调用,调用次数或良好的mock
- (目前)不支持“任何值”匹配器
- 并非所有失败场景都可以精确地报告失败发生的位置
用法
有一个名为 ExampleTests.swift
的示例测试文件。请在那里查找一些可以运行的测试。这些测试是针对一个类 Example
对一个mocked协作者 ExampleCollaborator
的。
下面的示例假设我们在mock这个协议:
protocol Frood {
func voidFunction(value: Int)
func function() -> String
func anotherFunction(value: String)
}
在您的测试代码中,您需要创建一个 MockFrood
,它扩展了 Frood
并采用了 Mock
。mock创建了一个 MockCallHandler
,并将所有调用转发给它。
public class MockFrood: Frood, Mock {
let callHandler: MockCallHandler
init(testCase: XCTestCase) {
callHandler = MockCallHandlerImpl(testCase)
}
override func voidFunction(int: Int) {
// the first argument is the value returned by the mock
// while setting expectations. In this case, we can use nil
// as it returns Void
callHandler.accept(nil, functionName: __FUNCTION__, args: int)
}
override func function() -> String {
// here, the return type is String, so the first argument
// is a String. Any String will do.
return callHandler.accept("", functionName: __FUNCTION__, args: nil) as! String
}
override func anotherFunction(value: String) {
callHandler.accept(nil, functionName: __FUNCTION__, args: value)
}
}
默认情况下,SwiftMock 可以匹配以下类型:
- String / String?
- Int / Int?
- Double / Double?
- Array / Array?
- Dictionary / Dictionary?
- 如果您发现问题中遗漏了任何常见类型,请提出一个问题。
由于Swift没有反射机制,所以SwiftMock无法自动匹配您的自定义类型,因此您需要为MockMatcher
编写一个扩展,使其适配MockMatcherExtension
。
extension MockMatcher: MockMatcherExtension {
public func match(item1: Any?, _ item2: Any?) -> Bool {
switch item1 {
case let first as MyCustomType:
if let second = item2 as? MyCustomType {
// custom matching code here //
return true
}
default:
return false
}
}
}
我可能会将模拟对象和自定义匹配器代码放在项目测试部分的独立组中。
当前支持的语法
// expect a call on a void function
mockObject.expect().call(mockObject.voidFunction(42))
...
mockObject.verify()
// expect a call on a function which returns a String value
mockObject.expect().call(mockObject.function()).andReturn("dent")
...
mockObject.verify()
// expect a call on a function which returns a String value, and also call a closure
mockObject.expect().call(mockObject.function()).andReturn("dent").andDo({
print("frood")
})
...
mockObject.verify()
// expect a call on a function, use a closure to return a String value
mockObject.expect().call(mockObject.function()).andReturnValue({ () in
return "dent"
})
...
mockObject.verify()
未来的功能
// expect a call with any String parameter
mockObject.expect().call(mockObject.anotherFunction(mockObject.anyString()))
...
mockObject.verify()
// expect a call with any String parameter, and capture it using a block
mockObject.expect().call(mockObject.anotherFunction(mockObject.anyString())).andCapture{ (parameters: Dictionary) in
// parameters dictionary contains the function parameters
})
...
mockObject.verify()
// stub a call
mockObject.stub().call(mockObject.function()).andReturn("dent")
// reject a call
mockObject.reject().call(mockObject.function())
当前模拟器是严格的,但随着优雅模拟器的出现,我们也可以支持较新的“在模拟后验证期望”风格的模拟。
// prod the system under test
systemUnderTest.prod()
// then verify that a function was called
mockObject.verify().call(mockObject.function())
...但是,我不认为我们可以把返回值反馈回系统中。嗯...
要求
- Xcode 7
- XCTest
安装
SwiftMock可以通过CocoaPods获得。要安装它,只需将以下行添加到您的Podfile中,针对测试目标进行操作
pod "SwiftMock"
反馈
欢迎提出问题和Pull Request - 特别欢迎对您可能发现的任何糟糕的Swift代码提出反馈 :-)
作者
马修·弗林特,[email protected]
许可协议
SwiftMock 在MIT许可下可用。有关更多信息,请参阅LICENSE文件。