在swift单元测试中快速模拟静态类方法?

在swift单元测试中快速模拟静态类方法?,swift,unit-testing,dependency-injection,xctest,swift-protocols,Swift,Unit Testing,Dependency Injection,Xctest,Swift Protocols,我是一名经验丰富的Objective-c程序员,但我不能对Swift说同样的话,我很难在没有使用OCMock等框架的情况下在Swift中对类进行单元测试 问题:我正在将Firebase集成到一个混合的Objective-C/Swift项目中,我需要根据应用程序的构建配置对其进行配置 我已经为此编写了一个Swift类(obj-c应用程序代表将使用该类),但是由于firebase框架是通过静态类方法配置的,精确地说是FIRApp.configure(with:FIROptions),我需要以某种方式

我是一名经验丰富的Objective-c程序员,但我不能对Swift说同样的话,我很难在没有使用OCMock等框架的情况下在Swift中对类进行单元测试

问题:我正在将Firebase集成到一个混合的Objective-C/Swift项目中,我需要根据应用程序的构建配置对其进行配置

我已经为此编写了一个Swift类(obj-c应用程序代表将使用该类),但是由于firebase框架是通过静态类方法配置的,精确地说是
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周围使用一个包装,但我想避免它,除非它是唯一可行的清洁解决方案
  • 我可以继续玩协议和进行依赖项反转,但是作为静态方法,我又在为语法而挣扎,我找不到一种简单的方法来使用静态方法的模拟类进行DI
作为参考(个人和可能需要它的人),这些是我发现有用的一些资源,我将继续挖掘:

在此期间,我们将非常感谢您的帮助


作为旁注,我有很多方法可以解决这个问题,而不必费劲模仿静态类方法,但我的目标是找到一种模仿它的方法,以便在测试更复杂的情况时更好地理解最佳实践。

您确实可以做其中任何一种

闭包参数 您可以让
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)
}
协议 您需要一个
可配置的
协议,然后在默认情况下使其符合
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)
  }
}
如果你只想在一个方法中使用它,它只是一个瞬态,它应该是一个函数参数,而不是存储的属性


(如果不清楚它是持久状态还是暂时状态,因为类的全部目的是调用单个函数,那么您甚至不需要类,只需要一个函数。)

最后,我不需要再处理这个问题,但无论如何,感谢您的回答;)。这是一个很难回答的问题,applier闭包有一个默认值,这个值是否连接到objective-c?我怀疑它没有像前面写的那样连接,但是如果必须的话,您可以通过一种可连接的方式完成同样的事情。