Flutter Firebase auth testing
Test your authentication functions with firebase
This one isn’t going to be too pretty and will more so give you guidance to get started. Unit testing firebase auth was not a fun time. I couldn’t find any clear up to date instructions on how to properly unit test so I made this to hopefully get you started.
There will be two files, signinscreen.dart which is where I have my auth functions and sign in screen widget. auth.dart under my tests section. I’ll mostly be giving you a template to use against the functions and will cover testing the actual widgets later
We’re going to make tests for these functions
Future<void> signInWithEmailPassword(
dynamic auth, String? email, String? password) async {
try {
await auth.signInWithEmailAndPassword(
email: email ?? "", password: password ?? "");
} on FirebaseAuthException catch (e) {
print(e.message);
if (e.code == 'user-not-found') {
print('No user found for that email.');
if (!Platform.environment.containsKey('FLUTTER_TEST')) {
showToast("No user found for that email.", ToastType.ERROR,
location: ToastGravity.BOTTOM);
}
} else if (e.code == 'wrong-password') {
print('Wrong password provided for that user.');
if (!Platform.environment.containsKey('FLUTTER_TEST')) {
showToast("Wrong password provided for that user.", ToastType.ERROR,
location: ToastGravity.BOTTOM);
}
}
}
}
Future<void> signInAsDev(dynamic auth, bool useMain) async {
try {
final String devUser =
useMain ? "test1@example.com" : "test2@example.com";
await auth.signInWithEmailAndPassword(
email: devUser, password: "Abcd_1234!");
} catch (e) {
print(e);
}
}
signInWithGoogle() async {
try {
// Trigger the authentication flow
final GoogleSignInAccount? googleUser = await GoogleSignIn().signIn();
// Obtain the auth details from the request
final GoogleSignInAuthentication? googleAuth =
await googleUser?.authentication;
// Create a new credential
final credential = GoogleAuthProvider.credential(
accessToken: googleAuth?.accessToken,
idToken: googleAuth?.idToken,
);
await FirebaseAuth.instance.signInWithCredential(credential);
} catch (e) {
print(e);
}
}
Future<void> signUpWithEmail(
dynamic auth, String? email, String? password, String? fullName) async {
try {
final credential = await auth.createUserWithEmailAndPassword(
email: email ?? "", password: password ?? "");
if (credential.user != null) {
if (fullName != null) {
credential.user?.updateDisplayName(fullName);
}
credential.user?.sendEmailVerification();
}
} on FirebaseAuthException catch (e) {
if (e.code == 'email-already-in-use') {
print('An account already exists for that email.');
if (!Platform.environment.containsKey('FLUTTER_TEST')) {
showToast(
"An account already exists for that email.", ToastType.ERROR,
Notice how each function takes in a dynamic auth
which will either be our mocked firebase auth class or the real one. The functions themselves are fairly straightforward. We pass in the parameters needed to initiate the auth function, if it fails we parse the error code thats we get.
These are a couple of scenarios that we can test with the above config. Due note that you may have to install a few packages listed at the top of this code block in order to get the tests to work. The magic happens in the setup()
block which is ran before we start the tests. We create a fake firebase auth user and then use it with all of our functions that we want to call in the test block
void main() {
group("Authentication", () {
late MaterialApp app;
late MockUser user;
late MockFirebaseAuth auth;
setUp(() {
user = MockUser(
isAnonymous: false,
uid: 'tester',
email: 'test@example.com',
displayName: 'Test',
);
auth = MockFirebaseAuth(signedIn: false, mockUser: user);
app = MaterialApp(
home: SignInScreen(primaryText: "Welcome", secondaryText: "Back"));
});
test("Can sign up with email and password", () {
SignInScreen signIn = SignInScreen();
signIn.signUpWithEmail(auth, user.email, "abcd_1234", user.displayName);
expect(auth.currentUser?.email, user.email);
});
test("Can sign in as dev", () {
SignInScreen signIn = SignInScreen();
signIn.signInAsDev(auth, true);
expect(auth.currentUser?.email, user.email);
});
test("Catches sign up email in use exception", () async {
auth = MockFirebaseAuth(signedIn: false, mockUser: user);
SignInScreen signIn = SignInScreen();
whenCalling(Invocation.method(#createUserWithEmailAndPassword, null))
.on(auth)
.thenThrow(FirebaseAuthException(code: 'email-already-in-use'));
expect(
() => auth.createUserWithEmailAndPassword(
email: user.email ?? "", password: ''),
throwsA(isA<FirebaseAuthException>()),
);
await signIn.signUpWithEmail(
auth, user.email, "abcd_1234", user.displayName);
expect(auth.currentUser, null);
});
test("Can sign in with email and password", () {
SignInScreen signIn = SignInScreen();
signIn.signInWithEmailPassword(auth, user.email, "abcd_1234");
expect(auth.currentUser, user);
});
test("Can fake google sign in", () {
SignInScreen signIn = SignInScreen();
whenCalling(Invocation.method(#signInWithGoogle, null))
.on(auth)
.thenThrow(
Exception("Failed to open redirect for sign in with google"));
signIn.signInWithGoogle();
});
test("Sign in user not found exception", () {
SignInScreen signIn = SignInScreen();
whenCalling(Invocation.method(#signInWithEmailAndPassword, null))
.on(auth)
.thenThrow(FirebaseAuthException(code: 'user-not-found'));
expect(
() => auth.signInWithEmailAndPassword(
email: user.email ?? "", password: ""),
throwsA(isA<FirebaseAuthException>()),
);
signIn.signInWithEmailPassword(auth, user.email, "abcd_1234");
expect(auth.currentUser, null);
});
test("Sign in wrong password exception", () {
SignInScreen signIn = SignInScreen();
whenCalling(Invocation.method(#signInWithEmailAndPassword, null))
.on(auth)
.thenThrow(FirebaseAuthException(code: 'wrong-password'));
expect(
() => auth.signInWithEmailAndPassword(
email: user.email ?? "", password: ""),
throwsA(isA<FirebaseAuthException>()),
);
signIn.signInWithEmailPassword(auth, user.email, "abcd_1234");
expect(auth.currentUser, null);
});