Home > Back-end >  How can I properly mock GoogleSignIn for testing?
How can I properly mock GoogleSignIn for testing?

Time:11-17

I'm learning about unit test for Flutter. I have a Sign In with Google functionality in my app as a package and I want to unit test it.

I'm half way there but I kinda stuck about what to do with this error.

'package:firebase_auth_platform_interface/src/providers/google_auth.dart': Failed assertion: line 43 pos 12: 'accessToken != null || idToken != null': At least one of ID token and access token is required
dart:core                                                                      _AssertionError._throwNew
package:firebase_auth_platform_interface/src/providers/google_auth.dart 43:12  GoogleAuthProvider.credential
package:firebase_auth_client/src/firebase_auth_client.dart 107:45              FirebaseAuthClient.signInWithGoogle
===== asynchronous gap ===========================
dart:async                                                                     _CustomZone.registerUnaryCallback
package:firebase_auth_client/src/firebase_auth_client.dart 97:26               FirebaseAuthClient.signInWithGoogle
test/src/firebase_auth_client_test.dart 101:30                                 main.<fn>.<fn>.<fn>

My test script look like this

class FakeUserCredential extends Fake implements UserCredential {}

class MockFirebaseAuth extends Mock implements FirebaseAuth {}

class MockGoogleSignIn extends Mock implements GoogleSignIn {}

class MockGoogleSignInAccount extends Mock implements GoogleSignInAccount {}

class MockGoogleSignInAuthentication extends Mock
    implements GoogleSignInAuthentication {}

class MockOAuthCredential extends Mock implements OAuthCredential {}

void main() {
  late FirebaseAuth firebaseAuth;
  late UserCredential userCredential;
  late FirebaseAuthClient firebaseAuthClient;
  late GoogleSignIn googleSignIn;
  late GoogleSignInAccount googleSignInAccount;
  late GoogleSignInAuthentication googleSignInAuthentication;
  late OAuthCredential oAuthCredential;

  setUp(() {
    firebaseAuth = MockFirebaseAuth();
    userCredential = FakeUserCredential();
    googleSignIn = MockGoogleSignIn();
    googleSignInAccount = MockGoogleSignInAccount();
    oAuthCredential = MockOAuthCredential();
    googleSignInAuthentication = MockGoogleSignInAuthentication();
    firebaseAuthClient = FirebaseAuthClient(
      auth: firebaseAuth,
      googleSignIn: googleSignIn,
    );
  });

  group('FirebaseAuthClient', () {
    // passing tests omitted...

    group('SignIn', () {
      // passing tests omitted...

      test('with google completes', () async {
        when(() => googleSignIn.signIn()).thenAnswer(
          (_) async => googleSignInAccount,
        );

        when(() => googleSignInAccount.authentication).thenAnswer(
          (_) async => googleSignInAuthentication,
        );

        when(
          () => firebaseAuth.signInWithCredential(oAuthCredential),
        ).thenAnswer((_) async => userCredential);

        expect(
          firebaseAuthClient.signInWithGoogle(),
          completes,
        );
      });

      // passing tests omitted...
    });

   // passing tests omitted...
  });
}

And this is the package I wrote.

import 'package:firebase_auth/firebase_auth.dart';
import 'package:google_sign_in/google_sign_in.dart';

/// {@template firebase_auth_client_exception}
/// Abstract class to handle the firebase auth client exceptions.
/// {@endtemplate}
abstract class FirebaseAuthClientException implements Exception {
  /// {@macro firebase_auth_client_exception}
  const FirebaseAuthClientException(this.error);

  /// The error which was caught.
  final Object error;
}

/// {@template firebase_sign_in_failure}
/// Thrown during the sign in process if a failure occurs.
/// {@endtemplate}
class FirebaseSignInFailure extends FirebaseAuthClientException {
  /// {@macro firebase_sign_in_failure}
  const FirebaseSignInFailure(super.error);

  /// Construct error messages from the given code.
  factory FirebaseSignInFailure.fromCode(String code) {
    switch (code) {
      case 'invalid-email':
        return const FirebaseSignInFailure(
          'Email address is invalid.',
        );
      case 'user-disabled':
        return const FirebaseSignInFailure(
          'Your account is disabled.',
        );
      case 'user-not-found':
        return const FirebaseSignInFailure(
          'Unable to find your account.',
        );
      case 'wrong-password':
        return const FirebaseSignInFailure(
          'You have entered the wrong password.',
        );
      default:
        return const FirebaseSignInFailure(
          'An unknown error occurred.',
        );
    }
  }

  @override
  String toString() => error.toString();
}

/// {@template firebase_sign_out_failure}
/// Thrown during the sign out process if a failure occurs.
/// {@endtemplate}
class FirebaseSignOutFailure extends FirebaseAuthClientException {
  /// {@macro firebase_sign_out_failure}
  const FirebaseSignOutFailure(super.error);
}

/// {@template firebase_auth_client}
/// Firebase auth client
/// {@endtemplate}
class FirebaseAuthClient {
  /// {@macro firebase_auth_client}
  const FirebaseAuthClient({
    required FirebaseAuth auth,
    required GoogleSignIn googleSignIn,
  })  : _auth = auth,
        _googleSignIn = googleSignIn;

  final FirebaseAuth _auth;
  final GoogleSignIn _googleSignIn;

  // unrelated methods omitted...

  /// Sign the user in using Google auth provider.
  Future<UserCredential> signInWithGoogle() async {
    try {
      final googleUser = await _googleSignIn.signIn();
      final googleAuth = await googleUser?.authentication;

      if (googleAuth == null) {
        Error.throwWithStackTrace(
          const FirebaseSignInFailure('Sign In Cancelled.'),
          StackTrace.current,
        );
      }

      final credential = GoogleAuthProvider.credential(
        accessToken: googleAuth.accessToken,
        idToken: googleAuth.idToken,
      );

      return await _auth.signInWithCredential(credential);
    } on FirebaseException catch (error, stackTrace) {
      Error.throwWithStackTrace(
        FirebaseSignInFailure.fromCode(error.code),
        stackTrace,
      );
    } catch (error, stackTrace) {
      Error.throwWithStackTrace(FirebaseSignInFailure(error), stackTrace);
    }
  }

  // unrelated methods omitted...
}

I once tried to override the properties of the MockGoogleSignInAuthentication like this, but it doesn't work.

class MockGoogleSignInAuthentication extends Mock
    implements GoogleSignInAuthentication {
  @override
  String? get idToken => 'fakeId';

  @override
  String? get accessToken => 'fakeToken';
}

Can somebody please point me to the right direction for this? Thanks in advance!

CodePudding user response:

You can use google_sign_in mocks package for google auth mock

CodePudding user response:

For those who are wondering about how I implement the test using the package mentioned by user SHYAM.

Here is how my full test script look like.

import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_auth_client/firebase_auth_client.dart';
import 'package:firebase_auth_mocks/firebase_auth_mocks.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:google_sign_in/google_sign_in.dart';
import 'package:google_sign_in_mocks/google_sign_in_mocks.dart';
import 'package:mocktail/mocktail.dart';

void main() {
  late FirebaseAuth firebaseAuth;
  late FirebaseAuthClient firebaseAuthClient;
  late GoogleSignIn googleSignIn;

  const email = '[email protected]';
  const password = 'password';

  setUp(() {
    final user = MockUser(email: email);
    firebaseAuth = MockFirebaseAuth(mockUser: user);
    googleSignIn = MockGoogleSignIn();
    firebaseAuthClient = FirebaseAuthClient(
      auth: firebaseAuth,
      googleSignIn: googleSignIn,
    );
  });

  group('FirebaseAuthClient', () {
    test('can be instantiated', () {
      expect(
        FirebaseAuthClient(
          auth: firebaseAuth,
          googleSignIn: googleSignIn,
        ),
        isNotNull,
      );
    });

    group('SignIn', () {
      test('with an email completes', () async {
        expect(
          firebaseAuthClient.signInWithEmail(email: email, password: password),
          completes,
        );
      });

      test('with an email throw FirebaseAuthSignInFailure', () async {
        when(
          () => firebaseAuth.signInWithEmailAndPassword(
            email: email,
            password: password,
          ),
        ).thenThrow(Exception('oops'));

        expect(
          firebaseAuthClient.signInWithEmail(email: email, password: password),
          throwsA(isA<FirebaseSignInFailure>()),
        );
      });

      test('with google completes', () async {
        expect(
          firebaseAuthClient.signInWithGoogle(),
          completes,
        );
      });

      test('with google throws FirebaseSignInFailure', () async {
        when(
          () => firebaseAuth.signInWithProvider(GoogleAuthProvider()),
        ).thenThrow(Exception('oops'));

        expect(
          firebaseAuthClient.signInWithGoogle(),
          throwsA(isA<FirebaseSignInFailure>()),
        );
      });
    });

    group('SignOut', () {
      test('on completes', () async {
        expect(
          firebaseAuthClient.signOut(),
          completes,
        );
      });

      test('throw FirebaseSignOutFailure', () async {
        when(
          () => firebaseAuth.signOut(),
        ).thenThrow(Exception('oops'));

        expect(
          firebaseAuthClient.signOut(),
          throwsA(isA<FirebaseSignOutFailure>()),
        );
      });
    });
  });
}

  • Related