我正试图找到一种方法来定义一个结构,使其内部持有非指针类型,并保持易于测试的能力。我不想使用协议,因为不是有实际的数据内部的结构指针将被持有。如果我决定使用ptr支持类型,我将以如下方式实现一段示例代码
protocol TimeGetable {
var currentTime: Date { get }
}
struct Chronometer {
private let timeGetter: TimeGetable
private var startTime: Date
init(timeGetter: TimeGetable) {
self.timeGetter = timeGetter
startTime = timeGetter.currentTime
}
mutating func reset() {
startTime = timeGetter.currentTime
}
func elapsedTime() -> TimeInterval {
timeGetter.currentTime.timeIntervalSince(startTime)
}
}
struct RealGetter: TimeGetable {
var currentTime: Date { Date() }
}
struct FakeGetter: TimeGetable {
var currentTime: Date {
// Do some magic here to ease testing
// Return the same value each time as an example
return Date.distantPast
}
}
在这种方法中,无论实现TimeGetable
的类/结构有多大,Chronometer的大小都保持不变。
MemoryLayout<Chronometer>.size // 48 bytes
如果我决定不测试我的代码,我会这样定义Chronometer
struct Chronometer {
private var startTime: Date
init(timeGetter: TimeGetable) {
startTime = Date()
}
mutating func reset() {
startTime = Date()
}
func elapsedTime() -> TimeInterval {
Date().currentTime.timeIntervalSince(startTime)
}
}
在这种情况下,如果Date
类型大小上升Chronometer也上升,因为它持有非引用类型。这有利于提高性能并避免缓存丢失。我唯一想到的办法是做如下的事情
#if TESTS
public typealias DateType = DateMock
#else
public typealias DateType = Date
#endif
struct Chronometer {
private var startTime: DateType
init(timeGetter: TimeGetable) {
startTime = DateType()
}
mutating func reset() {
startTime = DateType()
}
func elapsedTime() -> TimeInterval {
DateType().currentTime.timeIntervalSince(startTime)
}
}
我将在带有测试的目标中定义TESTS
标志,而在生产代码中不定义任何标记。这似乎是一个相当丑陋的解决方案。最重要的是,这意味着在我的生产代码中只提到用于测试目的的类/结构。有没有更好的办法?
也许你可以考虑这样做一个结构体:
struct Chronometer {
private let timeGetter: () -> Date
private var startTime: Date
init(timeGetter: @escaping () -> Date = Date.init) {
self.timeGetter = timeGetter
startTime = timeGetter()
}
mutating func reset() {
startTime = timeGetter()
}
func elapsedTime() -> TimeInterval {
timeGetter().timeIntervalSince(startTime)
}
}
mock/stub很简单。然后你可以在struct的init中调用这些函数来创建你关心的实际日期。在测试用例中,它将是某个固定的日期函数,在实际中,是正常的date。Init返回当前日期/时间