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']);
Author by
eliam-uzziel Calvo
Updated on December 31, 2022Comments
-
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 myRegistrationLoginForm
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 almost 3 yearsThank you! Can you edited this in please: var methods = await Future.value(['password']); if (methods.contains('password') && email == '[email protected]') {