Class 'MockFirebaseAuth' has no instance method 'fetchSignInMethodsForEmail' with matching arguments

102

The firebase_auth_mocks package currently does not support mocking the fetchSignInMethodsForEmail() method.

A suggestion to get past the error is to replace this line:

      var methods = await auth.fetchSignInMethodsForEmail(email);

with something like this:

      var methods = await Future.value(['password']);
Share:
102
eliam-uzziel Calvo
Author by

eliam-uzziel Calvo

Updated on December 31, 2022

Comments

  • eliam-uzziel Calvo
    eliam-uzziel Calvo over 1 year

    I can't figure out why I get this error when trying to run this widget test. I just want to mock a user that is in the database so when I type in an email, I either go to my PasswordFormLogin widget if the user email matches the user email that is in the mock user or then go to my RegistrationLoginForm widget.

    Debug Console:

    ══╡ EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK ╞══════════════════════════════════════════════════
    The following NoSuchMethodError was thrown running a test:
    Class 'MockFirebaseAuth' has no instance method 'fetchSignInMethodsForEmail' with matching
    arguments.
    Receiver: Instance of 'MockFirebaseAuth'
    Tried calling: fetchSignInMethodsForEmail("[email protected]")
    Found: fetchSignInMethodsForEmail(String) => Future<List<String>>
    
    When the exception was thrown, this was the stack:
    #0      Object.noSuchMethod (dart:core-patch/object_patch.dart:54:5)
    #1      MockFirebaseAuth.noSuchMethod (package:firebase_auth_mocks/src/firebase_auth_mocks_base.dart:70:56)
    #2      MockFirebaseAuth.fetchSignInMethodsForEmail (package:firebase_auth/src/firebase_auth.dart:226:24)
    #3      ApplicationState.verifyEmail (file:///C:/Users/calvo/Documents/flutter/projects/freegapp/test/LoginFlow_widget_test.dart:92:32)
    #4      LoginFlow.build.<anonymous closure> (package:freegapp/LoginFlow.dart:57:45)
    #5      _EmailFormState.build.<anonymous closure> (package:freegapp/src/LoginFlowStuff/EmailFormLogin.dart:73:40)
    #6      _EmailFormState.build.<anonymous closure> (package:freegapp/src/LoginFlowStuff/EmailFormLogin.dart:64:32)
    #7      _InkResponseState._handleTap (package:flutter/src/material/ink_well.dart:989:21)
    #8      GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:182:24)
    #9      TapGestureRecognizer.handleTapUp (package:flutter/src/gestures/tap.dart:607:11)
    #10     BaseTapGestureRecognizer._checkUp (package:flutter/src/gestures/tap.dart:296:5)
    #11     BaseTapGestureRecognizer.handlePrimaryPointer (package:flutter/src/gestures/tap.dart:230:7)
    #12     PrimaryPointerGestureRecognizer.handleEvent (package:flutter/src/gestures/recognizer.dart:475:9)
    #13     PointerRouter._dispatch (package:flutter/src/gestures/pointer_router.dart:93:12)
    #14     PointerRouter._dispatchEventToRoutes.<anonymous closure> (package:flutter/src/gestures/pointer_router.dart:138:9)
    #15     _LinkedHashMapMixin.forEach (dart:collection-patch/compact_hash.dart:397:8)
    #16     PointerRouter._dispatchEventToRoutes (package:flutter/src/gestures/pointer_router.dart:136:18)
    #17     PointerRouter.route (package:flutter/src/gestures/pointer_router.dart:122:7)
    #18     GestureBinding.handleEvent (package:flutter/src/gestures/binding.dart:439:19)
    #19     GestureBinding.dispatchEvent (package:flutter/src/gestures/binding.dart:419:22)
    #20     RendererBinding.dispatchEvent (package:flutter/src/rendering/binding.dart:287:11)
    #21     GestureBinding._handlePointerEventImmediately (package:flutter/src/gestures/binding.dart:374:7)
    #22     GestureBinding.handlePointerEvent (package:flutter/src/gestures/binding.dart:338:5)
    #23     TestWidgetsFlutterBinding.handlePointerEvent (package:flutter_test/src/binding.dart:507:13)
    #24     WidgetTester.sendEventToBinding.<anonymous closure> (package:flutter_test/src/widget_tester.dart:792:15)
    #25     WidgetTester.sendEventToBinding.<anonymous closure> (package:flutter_test/src/widget_tester.dart:791:39)
    #28     TestAsyncUtils.guard (package:flutter_test/src/test_async_utils.dart:71:41)
    #29     WidgetTester.sendEventToBinding (package:flutter_test/src/widget_tester.dart:791:27)
    #30     TestGesture.up.<anonymous closure> (package:flutter_test/src/test_pointer.dart:392:24)
    #31     TestGesture.up.<anonymous closure> (package:flutter_test/src/test_pointer.dart:390:39)
    #34     TestAsyncUtils.guard (package:flutter_test/src/test_async_utils.dart:71:41)
    #35     TestGesture.up (package:flutter_test/src/test_pointer.dart:390:27)
    #36     WidgetController.tapAt.<anonymous closure> (package:flutter_test/src/controller.dart:278:21)
    <asynchronous suspension>
    <asynchronous suspension>
    (elided 5 frames from dart:async and package:stack_trace)
    
    The test description was:
      EmailFormLogin
    ══════════════════════════════════════════════════════════════════════════════════════════════════
    

    LoginFlow_widget_test.dart:

    import 'package:flutter_test/flutter_test.dart';
    import 'package:firebase_auth_mocks/firebase_auth_mocks.dart';
    import 'package:flutter/material.dart';
    import 'package:firebase_auth/firebase_auth.dart';
    import 'package:provider/provider.dart';
    import 'package:firebase_core/firebase_core.dart';
    
    import 'package:freegapp/LoginFlow.dart';
    
    final tUser = MockUser(
      isAnonymous: false,
      uid: 'T3STU1D',
      email: '[email protected]',
      displayName: 'Bob Builder',
      phoneNumber: '0800 I CAN FIX IT',
      photoURL: 'http://photos.url/bobbie.jpg',
      refreshToken: 'some_long_token',
    );
    
    void main() {
    // TextField widgets require a Material widget ancestor.
    // In material design, most widgets are conceptually "printed" on a sheet of material. In Flutter's
    // material library, that material is represented by the Material widget. It is the Material widget
    // that renders ink splashes, for instance. Because of this, many material library widgets require that
    // there be a Material widget in the tree above them.
    // To introduce a Material widget, you can either directly include one, or use a widget that contains
    // Material itself, such as a Card, Dialog, Drawer, or Scaffold.
      testWidgets('EmailFormLogin', (WidgetTester tester) async {
        await tester.pumpWidget(ChangeNotifierProvider(
            create: (context) => ApplicationState(),
            builder: (context, _) => MaterialApp(
                title: 'Freegap',
                theme: ThemeData(
                  primarySwatch: Colors.blue,
                ),
                home: Consumer<ApplicationState>(
                    builder: (context, appState, _) => LoginFlow(
                        email: appState.email,
                        loginState: appState.loginState,
                        startLoginFlow: appState.startLoginFlow,
                        verifyEmail: appState.verifyEmail,
                        signInWithEmailAndPassword:
                            appState.signInWithEmailAndPassword,
                        cancelRegistration: appState.cancelRegistration,
                        registerAccount: appState.registerAccount,
                        signOut: appState.signOut,
                        key: Key('LoginFlow'))))));
        expect(find.byKey(Key('EmailFormLogin')), findsOneWidget);
        // Enter '[email protected]' into the TextField.
        await tester.enterText(find.byType(TextField), '[email protected]');
        await tester.tap(find.byType(ElevatedButton));
        expect(find.byKey(Key('EmailFormLogin')), findsOneWidget);
      });
    }
    
    class ApplicationState extends ChangeNotifier {
      ApplicationState() {
        init();
      }
      final auth = MockFirebaseAuth(mockUser: tUser);
    
      Future<void> init() async {
        await Firebase.initializeApp();
    
        auth.userChanges().listen((user) {
          if (user != null) {
            _loginState = ApplicationLoginState.loggedIn;
          } else {
            _loginState = ApplicationLoginState.loggedOut;
          }
          notifyListeners();
        });
      }
    
      ApplicationLoginState _loginState = ApplicationLoginState.loggedOut;
      ApplicationLoginState get loginState => _loginState;
    
      String? _email;
      String? get email => _email;
    
      void startLoginFlow() {
        _loginState = ApplicationLoginState.emailAddress;
        notifyListeners();
      }
    
      void verifyEmail(
        String email,
        void Function(FirebaseAuthException e) errorCallback,
      ) async {
        try {
          var methods = await auth.fetchSignInMethodsForEmail(email);
          if (methods.contains('password')) {
            _loginState = ApplicationLoginState.password;
          } else {
            _loginState = ApplicationLoginState.register;
          }
          _email = email;
          notifyListeners();
        } on FirebaseAuthException catch (e) {
          errorCallback(e);
        }
      }
    
      void signInWithEmailAndPassword(
        String email,
        String password,
        void Function(FirebaseAuthException e) errorCallback,
      ) async {
        try {
          await auth.signInWithEmailAndPassword(
            email: email,
            password: password,
          );
        } on FirebaseAuthException catch (e) {
          errorCallback(e);
        }
      }
    
      void cancelRegistration() {
        _loginState = ApplicationLoginState.loggedOut;
        notifyListeners();
      }
    
      void registerAccount(String email, String displayName, String password,
          void Function(FirebaseAuthException e) errorCallback) async {
        try {
          var credential = await auth.createUserWithEmailAndPassword(
              email: email, password: password);
          await credential.user!.updateDisplayName(displayName);
        } on FirebaseAuthException catch (e) {
          errorCallback(e);
        }
      }
    
      void signOut() {
        auth.signOut();
      }
    }
    

    LoginFlow.dart:

    import 'package:flutter/material.dart';
    import 'package:freegapp/src/LoginFlowStuff/EmailFormLogin.dart';
    import 'package:freegapp/src/LoginFlowStuff/PasswordFormLogin.dart';
    import 'package:freegapp/Sell.dart';
    import 'package:freegapp/src/LoginFlowStuff/RegisterFormLogin.dart';
    import 'package:freegapp/src/style_widgets.dart';
    
    enum ApplicationLoginState {
      loggedOut,
      emailAddress,
      register,
      password,
      loggedIn,
    }
    
    class LoginFlow extends StatelessWidget {
      const LoginFlow({
        required this.loginState,
        required this.email,
        required this.startLoginFlow,
        required this.verifyEmail,
        required this.signInWithEmailAndPassword,
        required this.cancelRegistration,
        required this.registerAccount,
        required this.signOut,
        Key? key,
      }) : super(key: key);
    
      final ApplicationLoginState loginState;
      final String? email;
      final void Function() startLoginFlow;
      //  typedef myFunction = final void Function(String email, void Function(Exception e) error,);
      final void Function(
        String email,
        void Function(Exception e) error,
      ) verifyEmail; //  myFunction verifyEmail() = {}
      final void Function(
        String email,
        String password,
        void Function(Exception e) error,
      ) signInWithEmailAndPassword;
      final void Function() cancelRegistration;
      final void Function(
        String email,
        String displayName,
        String password,
        void Function(Exception e) error,
      ) registerAccount;
      final void Function() signOut;
    
      @override
      Widget build(BuildContext context) {
        switch (loginState) {
          case ApplicationLoginState.loggedOut:
            return EmailFormLogin(
                key: Key('EmailFormLogin'),
                callback: (email) => verifyEmail(
                    email, (e) => _showErrorDialog(context, 'Invalid email', e)));
          case ApplicationLoginState.password:
            return PasswordFormLogin(
              key: Key('PasswordFormLogin'),
              email: email!,
              login: (email, password) {
                signInWithEmailAndPassword(email, password,
                    (e) => _showErrorDialog(context, 'Failed to sign in', e));
              },
            );
          case ApplicationLoginState.register:
            return RegisterFormLogin(
              key: Key('RegisterFormLogin'),
              email: email!,
              cancel: () {
                cancelRegistration();
              },
              registerAccount: (
                email,
                displayName,
                password,
              ) {
                registerAccount(
                    email,
                    displayName,
                    password,
                    (e) =>
                        _showErrorDialog(context, 'Failed to create account', e));
              },
            );
          case ApplicationLoginState.loggedIn:
            return Sell(
              logout: () {
                signOut();
              },
              key: Key('Sell'),
            );
          default:
            return Row(
              children: const [
                Text("Internal error, this shouldn't happen..."),
              ],
            );
        }
      }
    }
    
    void _showErrorDialog(BuildContext context, String title, Exception e) {
      showDialog<void>(
        context: context,
        builder: (context) {
          return AlertDialog(
            title: Text(
              title,
              style: const TextStyle(fontSize: 24),
            ),
            content: SingleChildScrollView(
              child: ListBody(
                children: <Widget>[
                  Text(
                    '${(e as dynamic).message}',
                    style: const TextStyle(fontSize: 18),
                  ),
                ],
              ),
            ),
            actions: <Widget>[
              StyledButton(
                onPressed: () {
                  Navigator.of(context).pop();
                },
                child: const Text(
                  'OK',
                  style: TextStyle(color: Colors.deepPurple),
                ),
              ),
            ],
          );
        },
      );
    }
    

    EmailFormLogin.dart:

    import 'package:flutter/material.dart';
    import 'package:freegapp/src/style_widgets.dart';
    
    class EmailFormLogin extends StatefulWidget {
      const EmailFormLogin({
        required this.callback,
        Key? key,
      }) : super(key: key);
      final void Function(String email) callback;
    
      @override
      _EmailFormState createState() => _EmailFormState();
    }
    
    class _EmailFormState extends State<EmailFormLogin> {
      // Create a global key that uniquely identifies the Form widget
      // and allows validation of the form.
      //
      // Note: This is a `GlobalKey<FormState>`,
      // not a GlobalKey<_EmailFormState>.
      final _formKey = GlobalKey<FormState>(debugLabel: '_EmailFormState');
      final _controller = TextEditingController();
    
      @override
      void dispose() {
        _controller.dispose();
    
        super.dispose();
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
            body: Column(children: <Widget>[
          Form(
            key: _formKey,
            child: Column(
              children: <Widget>[
                const Header('Sign in / Register'),
                Padding(
                  padding: const EdgeInsets.symmetric(horizontal: 24),
                  child: TextFormField(
                    controller: _controller,
                    keyboardType: TextInputType.emailAddress,
                    decoration: const InputDecoration(
                      hintText: 'Enter your email',
                      prefixIcon: Icon(Icons.mail),
                    ),
                    validator: (value) {
                      if (value!.isEmpty) {
                        return 'Enter your email address';
                      }
                      return null;
                    },
                  ),
                ),
                Row(
                  mainAxisAlignment: MainAxisAlignment.end,
                  children: <Widget>[
                    Padding(
                      padding: const EdgeInsets.symmetric(
                          vertical: 16.0, horizontal: 30),
                      child: ElevatedButton(
                        onPressed: () async {
                          // The FormState class contains the validate() method.
                          // When the validate() method is called, it runs the validator() function
                          // for each text field in the form. If everything looks good,
                          // the validate() method returns true. If any text field contains errors,
                          // the validate() method rebuilds the form to display any error messages and returns false.
                          // add ! to assert that it isn’t null (and to throw an exception if it is).
                          if (_formKey.currentState!.validate()) {
                            // If the form is valid,
                            widget.callback(_controller
                                .text); // call to parent with current string the user is editing
                          }
                        },
                        child: const Text('NEXT'),
                      ),
                    ),
                  ],
                ),
              ],
            ),
          ),
        ]));
      }
    }
    
  • eliam-uzziel Calvo
    eliam-uzziel Calvo almost 3 years
    Thank you! Can you edited this in please: var methods = await Future.value(['password']); if (methods.contains('password') && email == '[email protected]') {