我正在尝试对自己的类进行单元测试,该类正在第三方类上调用方法:

FIRAuth.auth()?.signInAnonymously() { (user, error) in
    //
}

我正在使用基于协议(protocol)的依赖项注入(inject)来实现此目的:
protocol FIRAuthProtocol {
    func signInAnonymously(completion: FIRAuthResultCallback?)
}
extension FIRAuth: FIRAuthProtocol {}

class MyClass {
    private var firAuth: FIRAuthProtocol

    init(firAuth: FIRAuthProtocol) {
        self.firAuth = firAuth
    }

    func signIn() {
        firAuth.signInAnonymously() { (user, error) in
            //
        }
    }
}

class MockFIRAuth: FIRAuthProtocol {
    var signInAnonymouslyCalled = false

    func signInAnonymously(completion: FIRAuthResultCallback? = nil) {
        signInAnonymouslyCalled = true
    }

}

class MyClassSpec: QuickSpec {
    override func spec() {
        describe("MyClass") {
            describe(".signIn()") {
                it("should call signInAnonymously() on firAuth") {
                    let mockFIRAuth = MockFIRAuth()
                    let myClass = MyClass(firAuth: mockFIRAuth)
                    expect(mockFIRAuth.signInAnonymouslyCalled).to(beFalse())
                    myClass.signIn()
                    expect(mockFIRAuth.signInAnonymouslyCalled).to(beTrue())
                }
            }
        }
    }
}

到现在为止还挺好!
现在,我希望我的 mock FIRAuth返回FIRUser的实例。
这是我的问题:我自己无法创建FIRUser的实例。

仅供引用:public typealias FIRAuthResultCallback = (FIRUser?, Error?) -> Swift.Void
如果找到这篇很棒的文章,它解释了如何在第三方类上使方法返回协议(protocol)而不是类型。 http://masilotti.com/testing-nsurlsession-input/
也许我的情况与本文的情况有所不同,但这是我对此的看法:

我定义了一个FIRUserProtocol:
protocol FIRUserProtocol {
    var uid: String { get }
}
extension FIRUser: FIRUserProtocol {}

我已经更新了FIRAuthProtocol以使用FIRUserProtocol而不是FIRUser调用完成处理程序:
protocol FIRAuthProtocol {
    func signInAnonymously(completion: ((FIRUserProtocol?, Error?) -> Void)?)
}

我已经更新了我的FIRAuth扩展以支持修改后的协议(protocol)。我新定义的方法调用signInAnonymously的默认实现:
extension FIRAuth: FIRAuthProtocol {
    func signInAnonymously(completion: ((FIRUserProtocol?, Error?) -> Void)? = nil) {
        signInAnonymously(completion: completion)
    }
}

最后,我更新了MockFIRAuth以支持修改后的协议(protocol):
class MockFIRAuth: FIRAuthProtocol {
    var signInAnonymouslyCalled = false
    func signInAnonymously(completion: ((FIRUserProtocol?, Error?) -> Void)? = nil) {
        signInAnonymouslyCalled = true
    }
}

现在,当我运行测试时,一切都崩溃了:

swift - 在Swift中模拟第三方类(class)(Firebase)-LMLPHPThread 1: EXC_BAD_ACCESS (code=2, address=0x7fff586a2ff8)
请指教!

更新

在我的FIRAuthProtocol方法中重命名完成参数标签后,一切似乎都按预期工作:
protocol FIRAuthProtocol {
    func signInAnonymously(completionWithProtocol: ((FIRUserProtocol?, Error?) -> Void)?)
}

extension FIRAuth: FIRAuthProtocol {
    func signInAnonymously(completionWithProtocol: ((FIRUserProtocol?, Error?) -> Void)? = nil) {
        signInAnonymously(completion: completionWithProtocol)
    }
}

目前,这可以解决我的问题,但是我仍然想知道为什么我的第一次尝试失败了。这是否意味着无法将两个在其闭包中具有不同参数类型的方法区分开来,这导致我的应用程序崩溃了?

最佳答案

我终于找到了一种解决此问题的优雅方法。

protocol FIRAuthProtocol {
    func signInAnonymously(completion: ((FIRUserProtocol?, Error?) -> Void)?)
}

extension FIRAuth: FIRAuthProtocol {
    func signInAnonymously(completion: ((FIRUserProtocol?, Error?) -> Void)? = nil) {
        let completion = completion as FIRAuthResultCallback?
        signInAnonymously(completion: completion)
    }
}

这样,无需更改函数名称或参数标签。

09-25 21:08