![](/img/trans.png)
[英]Is it really necessary to have custom implementation for 'Sign-in with Apple' on Firebase on iOS?
[英]Mocking iOS Firebase Auth sign-in methods
這個問題有點類似於Swift 中的 Mock 第三方類 (Firebase),但根據對它的答案,不同的地方足以保證提出一個新問題。
我正在嘗試模擬Auth
/ FIRAuth
方法signIn(withEmail email: String, password: String, completion: AuthDataResultCallback?)
並且在嘗試模擬AuthDataResultCallback
對象時遇到了困難,主要是因為它有一個User
屬性,我也想嘲笑。 不幸的是,我無法創建自己的User
或Auth
對象,因為它們已被標記為在 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
}
用法(使用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.
}
如果有人對如何實現這一點有更好的想法,請告訴我!
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.