簡體   English   中英

模擬 iOS Firebase Auth 登錄方法

[英]Mocking iOS Firebase Auth sign-in methods

這個問題有點類似於Swift 中的 Mock 第三方類 (Firebase),但根據對它的答案,不同的地方足以保證提出一個新問題。

我正在嘗試模擬Auth / FIRAuth方法signIn(withEmail email: String, password: String, completion: AuthDataResultCallback?)並且在嘗試模擬AuthDataResultCallback對象時遇到了困難,主要是因為它有一個User屬性,我想嘲笑。 不幸的是,我無法創建自己的UserAuth對象,因為它們已被標記為在 Swift 中沒有可用的初始化程序。

我有一個對象(我們稱之為UserAuthenticationRepository )負責執行用戶身份驗證和數據庫讀取。 我想向其中注入一個 Firebase auth 對象以在后台執行這些操作,但是由於我想測試這個存儲庫對象,所以我希望在進行單元測試時能夠注入一個 Firebase 模擬對象。

我想要做的是這樣的事情(這個問題稍微簡化了):

import FirebaseAuth

protocol FirebaseUserType {
    var uid: String { get }
}

extension User: FirebaseUserType {}

protocol FirebaseAuthDataResultType {
    var user: FirebaseUserType { get }
}

extension AuthDataResult: FirebaseAuthDataResultType {
    var user: FirebaseUserType {
        // This is where I'm running into problems because AuthDataResult expects a User object, 
        // which I also use in the UserAuthenticationRepository signIn(withEmail:) method
    }
}

protocol FirebaseAuthenticationType {
    func signIn(withEmail email: String, password: String, completion: ((FirebaseAuthDataResultType?, Error?) -> Void)?)
}

extension Auth: FirebaseAuthenticationType {
    func signIn(withEmail email: String, password: String, completion: ((FirebaseAuthDataResultType?, Error?) -> Void)?) {
        let completion = completion as AuthDataResultCallback?
        signIn(withEmail: email, password: password, completion: completion)
    }
}

protocol UserAuthenticationType {
    func loginUser(emailAddress: String, password: String) -> Observable<User>
}

class UserAuthenticationRepository: UserAuthenticationType {
    private let authenticationService: FirebaseAuthenticationType
    private let disposeBag = DisposeBag()

    init(authenticationService: FirebaseAuthenticationType = Auth.auth()) {
        self.authenticationService = authenticationService
    }

    func loginUser(emailAddress: String, password: String) -> Observable<User> {
        return .create { [weak self] observer in
            self?.authenticationService.signIn(withEmail: emailAddress, password: password, completion: { authDataResult, error in
                if let error = error {
                    observer.onError(error)
                } else if let authDataResult = authDataResult {
                    observer.onNext(authDataResult.user)
                }
            })
            return Disposables.create()
        }
    }

如上所述,當我嘗試擴展AuthDataResult以符合我的FirebaseAuthDataResultType協議時遇到了問題。 有可能做我想做的事嗎? 在測試UserAuthenticationRepository時,我最終想在我的 Firebase 身份驗證服務中傳回一個uid字符串。

我最終能夠找到一種方法來模擬我所需的 Firebase Auth對象,但我不得不訴諸於 Firebase User對象的子類化,向它添加在測試期間使用的新屬性(不能直接創建User對象或改變其屬性),然后創建一個符合FirebaseAuthDataResultType的結構,該結構在測試期間使用MockUser對象進行初始化。 我最終需要的協議和擴展如下:

protocol FirebaseAuthDataResultType {
    var user: User { get }
}

extension AuthDataResult: FirebaseAuthDataResultType {}

typealias FirebaseAuthDataResultTypeCallback = (FirebaseAuthDataResultType?, Error?) -> Void

protocol FirebaseAuthenticationType {
    func signIn(withEmail email: String, password: String, completion: FirebaseAuthDataResultTypeCallback?)
    func signOut() throws
    func addStateDidChangeListener(_ listener: @escaping AuthStateDidChangeListenerBlock) -> AuthStateDidChangeListenerHandle
    func removeStateDidChangeListener(_ listenerHandle: AuthStateDidChangeListenerHandle)
}

extension Auth: FirebaseAuthenticationType {
    func signIn(withEmail email: String, password: String, completion: FirebaseAuthDataResultTypeCallback?) {
        let completion = completion as AuthDataResultCallback?
        signIn(withEmail: email, password: password, completion: completion)
    }
}

下面是模擬對象:

class MockUser: User {
    let testingUID: String
    let testingEmail: String?
    let testingDisplayName: String?

    init(testingUID: String,
         testingEmail: String? = nil,
         testingDisplayName: String? = nil) {
        self.testingUID = testingUID
        self.testingEmail = testingEmail
        self.testingDisplayName = testingDisplayName
    }
}

struct MockFirebaseAuthDataResult: FirebaseAuthDataResultType {
    var user: User
}

帶有存根的模擬 Firebase 身份驗證服務的實例:

class MockFirebaseAuthenticationService: FirebaseAuthenticationType {
    typealias AuthDataResultType = (authDataResult: FirebaseAuthDataResultType?, error: Error?)
    var authDataResultFactory: (() -> (AuthDataResultType))?

    func signIn(withEmail email: String, password: String, completion: FirebaseAuthDataResultTypeCallback?) {
      // Mock service logic goes here
    }

    // ...rest of protocol functions
}

用法(使用RxSwiftRxTest ):

func testLoginUserReturnsUserIfSignInSuccessful() {
    let firebaseAuthService = MockFirebaseAuthenticationService()
    let expectedUID = "aM1RyjpaZcQ4EhaUvDAeCnla3HX2"
    firebaseAuthService.authDataResultFactory = {
        let user = MockUser(testingUID: expectedUID)
        let authDataResult = MockFirebaseAuthDataResult(user: user)
        return (authDataResult, nil)
    }

    let sut = UserSessionRepository(authenticationService: firebaseAuthService)
    let userObserver = testScheduler.createObserver(User.self)

    sut.loginUser(emailAddress: "john@gmail.com", password: "123456")
       .bind(to: userObserver)
       .disposed(by: disposeBag)

    testScheduler.start()

    let user = userObserver.events[0].value.element as? MockUser

    // Assert MockUser properties, events, etc.
}

如果有人對如何實現這一點有更好的想法,請告訴我!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM