模拟iOS Firebase Auth登录方法 [英] Mocking iOS Firebase Auth sign-in methods

查看:68
本文介绍了模拟iOS Firebase Auth登录方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这个问题有点类似于在Swift中模拟第三方类(Firebase) ,但根据答案的不同,足以保证有一个新问题.

This question is somewhat similar to Mock third party classes (Firebase) in Swift but different enough to warrant a new question, based on the answers to it.

我正在尝试模拟Auth/FIRAuth方法signIn(withEmail email: String, password: String, completion: AuthDataResultCallback?),并且在尝试模拟AuthDataResultCallback对象时遇到了困难,主要是因为它具有我User属性也想嘲笑.不幸的是,我无法创建自己的UserAuth对象,因为它们已被标记为在Swift中没有可用的初始化程序.

I'm trying to mock the Auth/FIRAuth method signIn(withEmail email: String, password: String, completion: AuthDataResultCallback?) and am running into difficulties with trying to mock the AuthDataResultCallback object, mainly because it has a User property that I also want to mock. Unfortunately, I'm not able to create my own User or Auth objects because they've been marked as not having an available initializer in Swift.

我有一个对象(我们称其为UserAuthenticationRepository),该对象负责执行用户身份验证和数据库读取.我想向其中注入Firebase身份验证对象以在后台进行这些操作,但是由于我想测试此存储库对象,因此我希望能够在进行单元测试时注入Firebase模拟对象.

I have an object (let's call it UserAuthenticationRepository) that's responsible for performing user authentication and database reads. I'd like to inject a Firebase auth object into it to do these things under the hood, but since I want to test this repository object I'd like to be able to inject a Firebase mock object when I go to unit test it.

我想做的是这样的(此问题略有简化):

What I want to do is something like this (simplified slightly for this question):

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字符串.

As noted above, I'm running into problems when I try to extend AuthDataResult to conform to my FirebaseAuthDataResultType protocol. Is it possible to do what I'm trying to do? I'd ultimately like to pass back a uid string in my Firebase authentication service when testing UserAuthenticationRepository.

推荐答案

我最终能够找到一种方法来模拟我所需的Firebase Auth对象,但是我不得不求助于Firebase User对象的子类化,向其添加新属性以在测试过程中使用(不能直接创建User对象或对其属性进行突变),然后创建一个符合FirebaseAuthDataResultType的结构,该结构在测试过程中使用MockUser对象进行了初始化.我最终需要的协议和扩展如下:

I was eventually able to find a way to mock the Firebase Auth objects necessary for me, but I had to resort to subclassing the Firebase User object, adding new properties to it be used during testing (can't create a User object directly or mutate its properties), and then creating a struct which conforms to FirebaseAuthDataResultType which is initialized with a MockUser object during testing. The protocols and extensions I ended up needing are below:

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身份验证服务的实例:

An instance of my mock Firebase authentication service with stubs:

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

如果有人对如何实现此目标有更好的想法,请告诉我!

If anyone has any better ideas of how this can be accomplished, please let me know!

这篇关于模拟iOS Firebase Auth登录方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆