如何测试应该使 FirebaseAuth.instance.userChanges() 调用其侦听器的 FirebaseAuth.instance.signOut() 的效果?
How to test the effect of FirebaseAuth.instance.signOut() that should make FirebaseAuth.instance.userChanges() invoke its listener?
我正在尝试在 Get to know Firebase for Flutter 代码实验室中编写 ApplicationState 的代码,同时练习测试驱动开发。
codelab中的方法signOut应该是这样的:
void signOut() {
// The question is about how to test the effect of this invocation
FirebaseAuth.instance.signOut();
}
如果我理解正确,FirebaseAuth.instance.signOut()
应该使 FirebaseAuth.instance.userChanges()
使用包含 null
User
的 Stream<User?>
调用其侦听器。所以调用 FirebaseAuth.instance.signOut()
的效果并不直接。我不知道如何嘲笑这个。根据测试驱动开发,我永远不应该编写代码,除非我这样做是为了让(失败的测试)通过。
问题是如何编写强制我编写以下代码的失败测试:
Future<void> init() async {
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
FirebaseAuth.instance.userChanges().listen((user) {
if (user != null) {
_loginState = ApplicationLoginState.loggedIn;
}
////////////////////////////////////////////////////////////
else {
_loginState = ApplicationLoginState.loggedOut; ////
}
////////////////////////////////////////////////////////////
notifyListeners();
});
}
我可以这样模拟 FirebaseAuth.instance.signOut()
:
// firebaseAuth is a mocked object
when(firebaseAuth.signOut())
.thenAnswer((realInvocation) => Completer<void>().future);
// sut (system under test)
sut.signOut();
verify(firebaseAuth.signOut()).called(1);
这迫使我调用 FirebaseAuth.instance.signOut()
。但这并不强迫我写上述代码让它通过。
我测试这段代码:
Future<void> init() async {
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
FirebaseAuth.instance.userChanges().listen((user) {
/////////////////////////////////////////////////////////////
if (user != null) {
_loginState = ApplicationLoginState.loggedIn; ////
}
/////////////////////////////////////////////////////////////
else {
_loginState = ApplicationLoginState.loggedOut;
}
notifyListeners();
});
}
通过嘲讽FirebaseAuth.instance.signInWithEmailAndPassword()
:
final userCredential = MockUserCredential();
// firebaseAuth is a mocked object
when(firebaseAuth.signInWithEmailAndPassword(
email: validEmail, password: password))
.thenAnswer((realInvocation) => Future.value(userCredential));
// sut (system under test)
await sut.signInWithEmailAndPassword(
validEmail, password, firebaseAuthExceptionCallback);
// This is the direct effect on my class, that will happen by the aforementioned code
expect(sut.loginState, ApplicationLoginState.loggedIn);
请耐心等待我。我是测试和测试驱动开发的新手。
这是我所做的。
我没有像代码实验室那样在 init() 方法中调用一次 FirebaseAuth.instance.userChanges().listen()
,而是调用了两次,一次是在 signInWithEmailAndPassword() 方法上,一次是在 signOut() 方法上。
Future<void> signInWithEmailAndPassword(String email, String password,
void Function(FirebaseAuthException exception) errorCallback) async {
///////////////////////////////////////////////////////////////////
firebaseAuth.userChanges().listen(_whenNotNullUser); ////
///////////////////////////////////////////////////////////////////
try {
await firebaseAuth.signInWithEmailAndPassword(
email: email, password: password);
} on FirebaseAuthException catch (exception) {
errorCallback(exception);
}
}
Future<void> signOut() async {
///////////////////////////////////////////////////////////////////
firebaseAuth.userChanges().listen(_whenNullUser); ////
///////////////////////////////////////////////////////////////////
await firebaseAuth.signOut();
}
void _whenNullUser(User? user) {
if (user == null) {
_loginState = ApplicationLoginState.loggedOut;
notifyListeners();
}
}
void _whenNotNullUser(User? user) {
if (user != null) {
_loginState = ApplicationLoginState.loggedIn;
notifyListeners();
}
}
这是我的测试:
test("""
$given $workingWithApplicationState
$wheN Calling signInWithEmailAndPassword()
$and Calling loginState returns ApplicationLoginState.loggedIn
$and Calling signOut()
$and Calling loginState returns ApplicationLoginState.loggedOut
$and Calling signInWithEmailAndPassword()
$then Calling loginState should return ApplicationLogginState.loggedIn
$and $notifyListenersCalled
""", () async {
when(firebaseAuth.signInWithEmailAndPassword(
email: validEmail, password: password))
.thenAnswer((realInvocation) => Future.value(userCredential));
await sut.signInWithEmailAndPassword(
validEmail, password, firebaseAuthExceptionCallback);
expect(sut.loginState, ApplicationLoginState.loggedIn);
reset(notifyListenerCall);
prepareUserChangesForTest(nullUser);
await sut.signOut();
verify(firebaseAuth.signOut()).called(1);
expect(sut.loginState, ApplicationLoginState.loggedOut);
reset(notifyListenerCall);
prepareUserChangesForTest(notNullUser);
when(firebaseAuth.signInWithEmailAndPassword(
email: validEmail, password: password))
.thenAnswer((realInvocation) => Future.value(userCredential));
await sut.signInWithEmailAndPassword(
validEmail, password, firebaseAuthExceptionCallback);
expect(sut.loginState, ApplicationLoginState.loggedIn);
verify(notifyListenerCall()).called(1);
});
现在我不得不在 _whenNullUser() 和 _whenNotNullUser() 方法中编写登录以通过我的测试。
我正在尝试在 Get to know Firebase for Flutter 代码实验室中编写 ApplicationState 的代码,同时练习测试驱动开发。 codelab中的方法signOut应该是这样的:
void signOut() {
// The question is about how to test the effect of this invocation
FirebaseAuth.instance.signOut();
}
如果我理解正确,FirebaseAuth.instance.signOut()
应该使 FirebaseAuth.instance.userChanges()
使用包含 null
User
的 Stream<User?>
调用其侦听器。所以调用 FirebaseAuth.instance.signOut()
的效果并不直接。我不知道如何嘲笑这个。根据测试驱动开发,我永远不应该编写代码,除非我这样做是为了让(失败的测试)通过。
问题是如何编写强制我编写以下代码的失败测试:
Future<void> init() async {
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
FirebaseAuth.instance.userChanges().listen((user) {
if (user != null) {
_loginState = ApplicationLoginState.loggedIn;
}
////////////////////////////////////////////////////////////
else {
_loginState = ApplicationLoginState.loggedOut; ////
}
////////////////////////////////////////////////////////////
notifyListeners();
});
}
我可以这样模拟 FirebaseAuth.instance.signOut()
:
// firebaseAuth is a mocked object
when(firebaseAuth.signOut())
.thenAnswer((realInvocation) => Completer<void>().future);
// sut (system under test)
sut.signOut();
verify(firebaseAuth.signOut()).called(1);
这迫使我调用 FirebaseAuth.instance.signOut()
。但这并不强迫我写上述代码让它通过。
我测试这段代码:
Future<void> init() async {
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
FirebaseAuth.instance.userChanges().listen((user) {
/////////////////////////////////////////////////////////////
if (user != null) {
_loginState = ApplicationLoginState.loggedIn; ////
}
/////////////////////////////////////////////////////////////
else {
_loginState = ApplicationLoginState.loggedOut;
}
notifyListeners();
});
}
通过嘲讽FirebaseAuth.instance.signInWithEmailAndPassword()
:
final userCredential = MockUserCredential();
// firebaseAuth is a mocked object
when(firebaseAuth.signInWithEmailAndPassword(
email: validEmail, password: password))
.thenAnswer((realInvocation) => Future.value(userCredential));
// sut (system under test)
await sut.signInWithEmailAndPassword(
validEmail, password, firebaseAuthExceptionCallback);
// This is the direct effect on my class, that will happen by the aforementioned code
expect(sut.loginState, ApplicationLoginState.loggedIn);
请耐心等待我。我是测试和测试驱动开发的新手。
这是我所做的。
我没有像代码实验室那样在 init() 方法中调用一次 FirebaseAuth.instance.userChanges().listen()
,而是调用了两次,一次是在 signInWithEmailAndPassword() 方法上,一次是在 signOut() 方法上。
Future<void> signInWithEmailAndPassword(String email, String password,
void Function(FirebaseAuthException exception) errorCallback) async {
///////////////////////////////////////////////////////////////////
firebaseAuth.userChanges().listen(_whenNotNullUser); ////
///////////////////////////////////////////////////////////////////
try {
await firebaseAuth.signInWithEmailAndPassword(
email: email, password: password);
} on FirebaseAuthException catch (exception) {
errorCallback(exception);
}
}
Future<void> signOut() async {
///////////////////////////////////////////////////////////////////
firebaseAuth.userChanges().listen(_whenNullUser); ////
///////////////////////////////////////////////////////////////////
await firebaseAuth.signOut();
}
void _whenNullUser(User? user) {
if (user == null) {
_loginState = ApplicationLoginState.loggedOut;
notifyListeners();
}
}
void _whenNotNullUser(User? user) {
if (user != null) {
_loginState = ApplicationLoginState.loggedIn;
notifyListeners();
}
}
这是我的测试:
test("""
$given $workingWithApplicationState
$wheN Calling signInWithEmailAndPassword()
$and Calling loginState returns ApplicationLoginState.loggedIn
$and Calling signOut()
$and Calling loginState returns ApplicationLoginState.loggedOut
$and Calling signInWithEmailAndPassword()
$then Calling loginState should return ApplicationLogginState.loggedIn
$and $notifyListenersCalled
""", () async {
when(firebaseAuth.signInWithEmailAndPassword(
email: validEmail, password: password))
.thenAnswer((realInvocation) => Future.value(userCredential));
await sut.signInWithEmailAndPassword(
validEmail, password, firebaseAuthExceptionCallback);
expect(sut.loginState, ApplicationLoginState.loggedIn);
reset(notifyListenerCall);
prepareUserChangesForTest(nullUser);
await sut.signOut();
verify(firebaseAuth.signOut()).called(1);
expect(sut.loginState, ApplicationLoginState.loggedOut);
reset(notifyListenerCall);
prepareUserChangesForTest(notNullUser);
when(firebaseAuth.signInWithEmailAndPassword(
email: validEmail, password: password))
.thenAnswer((realInvocation) => Future.value(userCredential));
await sut.signInWithEmailAndPassword(
validEmail, password, firebaseAuthExceptionCallback);
expect(sut.loginState, ApplicationLoginState.loggedIn);
verify(notifyListenerCall()).called(1);
});
现在我不得不在 _whenNullUser() 和 _whenNotNullUser() 方法中编写登录以通过我的测试。