代码之家  ›  专栏  ›  技术社区  ›  Evan R

模拟iOS Firebase Auth登录方法

  •  0
  • Evan R  · 技术社区  · 6 年前

    这个问题有点类似于 Mock third party classes (Firebase) in Swift

    我在嘲笑 Auth FIRAuth 方法 signIn(withEmail email: String, password: String, completion: AuthDataResultCallback?) 我在试图嘲笑 AuthDataResultCallback 对象,主要是因为它有 User 我的财产 想要嘲笑。不幸的是,我无法创建自己的 认证 对象,因为它们在Swift中被标记为没有可用的初始值设定项。

    UserAuthenticationRepository )它负责执行用户身份验证和数据库读取。我想向其中注入一个Firebase auth对象,以便在引擎盖下执行这些操作,但是由于我想测试这个repository对象,所以我想在进行单元测试时能够注入一个Firebase mock对象。

    我想做的是这样的事情(为这个问题稍微简化一下):

    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 协议。有可能做我想做的事吗?我最终想把 uid 用户身份验证存储库

    0 回复  |  直到 6 年前
        1
  •  0
  •   Evan R    6 年前

    我终于找到了一种方法来模拟这个火场 Auth 对象对我来说是必要的,但是我不得不使用Firebase的子类 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
    }
    

    RxSwift RxTest ):

    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.
    }
    

    如果有人对如何实现这一点有更好的想法,请告诉我!