在 swift 单元测试中以 swifty 方式模拟静态 class 方法?
Mocking a static class method in a swift unit test in a swifty way?
我是一名经验丰富的 Objective-c 程序员,但我不能对 Swift 说同样的话,我很难在 [=61] 中对 class 进行单元测试=] 不使用像 OCMock 这样的框架。
问题:我正在将 Firebase 集成到一个混合的 Objective-C/Swift 项目中,我需要根据应用程序的构建配置对其进行配置。
我已经为此编写了 Swift class(将由 obj-c 应用程序委托使用),但是由于 firebase 框架是通过静态 class 方法,准确地说 FIRApp.configure(with: FIROptions)
,我需要以某种方式模拟此方法以便对其进行单元测试。
我的代码,没有任何依赖注入句柄,看起来像这样:
@objc class FirebaseConfigurator: NSObject{
func configureFirebase(){
let config = configManager.buildConfiguration
var optionsPlistBaseName = getPlistName()
let optionsFile = Bundle.main.path(forResource: optionsPlistBaseName, ofType: "plist")
guard let opts = FIROptions(contentsOfFile: optionsFile) else{
assert(false, "fatal: unable to load \(optionsFile)")
return
}
FIRApp.configure(with: opts)
}
func getPlistName() -> String{
// retrieves correct plist name and returns it
}
}
我已经做了一些研究,但到目前为止我没有找到任何适合我的解决方案,但是我正在考虑以下之一:
- 我可以传递一个默认为
FIRApp.configure(with:)
的函数,但是我应该从 objective-c 执行此操作并且该函数还接受一个参数,我在语法上苦苦挣扎
- 我可以在 FIRApp 周围使用包装器,但我想避免使用它,除非是唯一可行的干净解决方案。
- 我可以继续使用协议并进行依赖倒置,但是作为静态方法我又在语法上苦苦挣扎,我找不到一种简单的方法来使用模拟 class 进行 DI静态方法。
作为参考(个人和可能需要它的人),这些是我发现有用的一些资源,我将继续挖掘这些资源:
与此同时,我们将不胜感激。
作为旁注,有很多方法可以解决这个问题,而不必费力地模拟静态 class 方法,但我的目标是找到一种模拟它的方法,以便获得更好的效果了解测试更复杂情况时的最佳实践。
你确实可以做到这些。
闭包参数
您可以让 configureFirebase
函数采用默认为您最初使用的 "applier" 闭包:
func configureFirebase(
using apply: (_ options: FIROptions) -> Void
= { opts in FIRApp.configure(opts) }
) {
// building |opts| as before
// Now replace this: FIRApp.configure(with: opts)
apply(opts)
}
协议
您需要一个 Configurable
协议,然后在默认情况下 FIRApp
遵守它:
protocol Configurable {
static func configure(with options: FIROptions)
}
extension FIRApp: Configurable {}
class FirebaseConfigurator {
var configurable: Configurable
init(configurable: Configurable = FIRApp) {
self.configurable = configurable
}
func configureFirebase() {
//load |opts|…
configurable.configure(with: opts)
}
}
如果你只是打算在一种方法中使用它,那么它只是瞬态,它应该是一个函数参数而不是存储的 属性。
(如果不清楚它是持久状态还是瞬态,因为 class 的全部意义在于调用单个函数,也许您甚至不需要 class,只需要一个函数.)
我是一名经验丰富的 Objective-c 程序员,但我不能对 Swift 说同样的话,我很难在 [=61] 中对 class 进行单元测试=] 不使用像 OCMock 这样的框架。
问题:我正在将 Firebase 集成到一个混合的 Objective-C/Swift 项目中,我需要根据应用程序的构建配置对其进行配置。
我已经为此编写了 Swift class(将由 obj-c 应用程序委托使用),但是由于 firebase 框架是通过静态 class 方法,准确地说 FIRApp.configure(with: FIROptions)
,我需要以某种方式模拟此方法以便对其进行单元测试。
我的代码,没有任何依赖注入句柄,看起来像这样:
@objc class FirebaseConfigurator: NSObject{
func configureFirebase(){
let config = configManager.buildConfiguration
var optionsPlistBaseName = getPlistName()
let optionsFile = Bundle.main.path(forResource: optionsPlistBaseName, ofType: "plist")
guard let opts = FIROptions(contentsOfFile: optionsFile) else{
assert(false, "fatal: unable to load \(optionsFile)")
return
}
FIRApp.configure(with: opts)
}
func getPlistName() -> String{
// retrieves correct plist name and returns it
}
}
我已经做了一些研究,但到目前为止我没有找到任何适合我的解决方案,但是我正在考虑以下之一:
- 我可以传递一个默认为
FIRApp.configure(with:)
的函数,但是我应该从 objective-c 执行此操作并且该函数还接受一个参数,我在语法上苦苦挣扎 - 我可以在 FIRApp 周围使用包装器,但我想避免使用它,除非是唯一可行的干净解决方案。
- 我可以继续使用协议并进行依赖倒置,但是作为静态方法我又在语法上苦苦挣扎,我找不到一种简单的方法来使用模拟 class 进行 DI静态方法。
作为参考(个人和可能需要它的人),这些是我发现有用的一些资源,我将继续挖掘这些资源:
与此同时,我们将不胜感激。
作为旁注,有很多方法可以解决这个问题,而不必费力地模拟静态 class 方法,但我的目标是找到一种模拟它的方法,以便获得更好的效果了解测试更复杂情况时的最佳实践。
你确实可以做到这些。
闭包参数
您可以让 configureFirebase
函数采用默认为您最初使用的 "applier" 闭包:
func configureFirebase(
using apply: (_ options: FIROptions) -> Void
= { opts in FIRApp.configure(opts) }
) {
// building |opts| as before
// Now replace this: FIRApp.configure(with: opts)
apply(opts)
}
协议
您需要一个 Configurable
协议,然后在默认情况下 FIRApp
遵守它:
protocol Configurable {
static func configure(with options: FIROptions)
}
extension FIRApp: Configurable {}
class FirebaseConfigurator {
var configurable: Configurable
init(configurable: Configurable = FIRApp) {
self.configurable = configurable
}
func configureFirebase() {
//load |opts|…
configurable.configure(with: opts)
}
}
如果你只是打算在一种方法中使用它,那么它只是瞬态,它应该是一个函数参数而不是存储的 属性。
(如果不清楚它是持久状态还是瞬态,因为 class 的全部意义在于调用单个函数,也许您甚至不需要 class,只需要一个函数.)