测试已测试 | ✓ |
Lang语言 | SwiftSwift |
许可证 | Apache 2 |
发布最新发布 | 2016年9月 |
SPM支持SPM | ✗ |
由Felix Jendrusch、Christian Hoffmann、Susann Proszak维护。
Dobby提供了用于模拟和存根的一些助手。
匹配器可以与值匹配,作为模拟和存根的基本构建块。有许多函数可以帮助为(等价)类型创建匹配器,包括可选值、元组、数组以及包含等价元素的字典
matches { $0 == value } // matches value
any() // matches anything
not(0) // matches anything but 0
none() // matches Optional<T>.None (nil)
some(1) // matches Optional<T>.Some(1)
equals(1) // matches 1
equals((1, 2)) // matches (1, 2)
equals((1, 2, 3)) // matches (1, 2, 3)
equals((1, 2, 3, 4)) // matches (1, 2, 3, 4)
equals((1, 2, 3, 4, 5)) // matches (1, 2, 3, 4, 5)
equals([1, 2, 3]) // matches [1, 2, 3]
equals([1: 1, 2: 2, 3: 3]) // matches [1: 1, 2: 2, 3: 3]
匹配器也可以嵌套
matches((matches { $0 == 0 }, any(), 2)) // matches (0, _, 2)
matches((not(equals(3)), some(any()))) // matches (not(3), _)
matches([any(), equals(4)]) // matches [_, 4]
matches(["key": matches { $0 == 5 }]) // matches ["key": 5]
模拟对象可用于验证所有设置好的期望都得到了实现。
默认情况下,模拟对象是严格的,期望的顺序很重要,这意味着必须预期所有交互,并且它们必须以期望的顺序发生
let mock = Mock<[Int]>()
mock.expect(matches([any(), matches { $0 > 0 }])) // expects [_, n > 0]
mock.record([0, 1]) // succeeds
mock.verify() // succeeds
mock.record([1, 0]) // unexpected, fails (fast)
也可以忽略期望的顺序
let mock = Mock<[Int]>(ordered: false)
mock.expect(matches([0, 1]))
mock.expect(matches([1, 0]))
mock.record([1, 0]) // succeeds
mock.record([0, 1]) // succeeds
mock.verify() // succeeds
mock.record([0, 0]) // unexpected, fails (fast)
温和的模拟对象允许存在意外的交互,同时仍然尊重期望的顺序
let mock = Mock<[Int?]>(strict: false)
mock.expect(matches([some(0)]))
mock.expect(matches([some(any())]))
mock.record([nil]) // unexpected, ignored
mock.record([1]) // out of order, ignored
mock.record([0]) // succeeds
mock.verify() // fails
mock.record([1]) // succeeds
mock.verify() // succeeds
当然,温和的模拟对象也可以忽略期望的顺序
let mock = Mock<[String: Int?]>(strict: false, ordered: false)
mock.expect(matches(["zero": some(0)]))
mock.expect(matches(["none": none()]))
mock.record(["some": 1]) // unexpected, ignored
mock.record(["none": nil]) // succeeds
mock.record(["zero": 0]) // succeeds
mock.verify() // succeeds
除了常规期望之外,温和的模拟对象还允许设置负期望
let mock = Mock<Int>(nice: true)
mock.reject(0)
mock.record(0) // rejected, fails (fast)
验证也可以延时进行,允许期望异步得到实现
let mock = Mock<Int>()
mock.expect(1)
let when = dispatch_time(DISPATCH_TIME_NOW, Int64(1.0 * Double(NSEC_PER_SEC)))
dispatch_after(when, dispatch_get_main_queue()) {
mock.record(1) // succeeds
}
mock.verifyWithDelay(2.0) // succeeds
当调用存根时,根据其设置的行为返回一个值,如果交互是意外的,则抛出错误。行为按顺序匹配,这意味着与第一个匹配到交互的匹配器关联的函数或返回值将被调用/返回
let stub = Stub<(Int, Int), Int>()
let behavior = stub.on(equals((2, 3)), returnValue: 4)
stub.on(matches((any(), any()))) { x, y in x * y }
try! stub.invoke((2, 3)) // returns 4
try! stub.invoke((3, 3)) // returns 9
行为也可以被清理
behavior.dispose()
try! stub.invoke((2, 3)) // returns 6
提供的模拟和存根助手可以与任何测试方法一起使用,包括协议测试实现、测试子类等。例如,假设您想验证与以下类的交互并更改其行为。
class MyClass {
func myMethod(fst: String, _ snd: String) -> String {
return fst + snd
}
}
为给定的类编写测试子类非常简单。
class MyClassMock: MyClass {
let myMethodMock = Mock<(String, String)>()
let myMethodStub = Stub<(String, String), String>()
override func myMethod(fst: String, _ snd: String) -> String {
myMethodMock.record((fst, snd))
// Throw an exception if the stub doesn't define any behavior for the interaction.
return try! myMethodStub.invoke((fst, snd))
}
}
测试子类可以让您验证所有设置期望都已满足,并允许您动态更改其行为。
let myClassMock = MyClassMock()
myClassMock.myMethodMock.expect(matches(("Hello", "World")))
myClassMock.myMethodStub.on(any()) { fst, snd in fst }
myClassMock.myMethod("Hello", "World") // returns "Hello"
myClassMock.myMethodMock.verify() // succeeds
如果您需要使用具有不同类型交互的模拟或存根,请考虑使用可比较的枚举来定义这些交互。
Dobby诞生于trivago。