Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Widget test fails with No MediaQuery widget found

My question is about flutter widget test, what is proper way to test existing widgets wrapped new Scaffold(...)? I have found MediaQuery.of but it accepts BuildContext instead of Widget.

Details: I have wrote simple login form widget and trying to implement widget test for it. After executing test i got exception:

Expected: 'Sorry, only customer can login from mobile device. [Mock]'   Actual: FlutterError:<No MediaQuery widget found.           Scaffold widgets require a MediaQuery widget ancestor.           The specific widget that could not find a MediaQuery ancestor was:             Scaffold-[LabeledGlobalKey<ScaffoldState>#8ffee]           The ownership chain for the affected widget is:             Scaffold-[LabeledGlobalKey<ScaffoldState>#8ffee] ← LoginForm ← [root]           Typically, the MediaQuery widget is introduced by the MaterialApp or WidgetsApp widget at           the top of your application widget tree.>    Which: FlutterError:<No MediaQuery widget found.           Scaffold widgets require a MediaQuery widget ancestor.           The specific widget that could not find a MediaQuery ancestor was:             Scaffold-[LabeledGlobalKey<ScaffoldState>#8ffee]           The ownership chain for the affected widget is:             Scaffold-[LabeledGlobalKey<ScaffoldState>#8ffee] ← LoginForm ← [root]           Typically, the MediaQuery widget is introduced by the MaterialApp or WidgetsApp widget at           the top of your application widget tree.>is not a string  When the exception was thrown, this was the stack: #4      main.<anonymous closure> (C:\Work\app_mobile\test\login_widget_test.dart:21:5) <asynchronous suspension> #5      testWidgets.<anonymous closure>.<anonymous closure>.<anonymous closure> (package:flutter_test\src\widget_tester.dart:61:25) #6      TestWidgetsFlutterBinding._runTestBody (package:flutter_test\src\binding.dart:471:19) <asynchronous suspension> #9      TestWidgetsFlutterBinding._runTest (package:flutter_test\src\binding.dart:458:14) #10     AutomatedTestWidgetsFlutterBinding.runTest.<anonymous closure> (package:flutter_test\src\binding.dart:640:24) #11     _FakeAsync.run.<anonymous closure> (package:quiver\testing\src\async\fake_async.dart:186:24) #15     _FakeAsync.run (package:quiver\testing\src\async\fake_async.dart:185:11) #16     AutomatedTestWidgetsFlutterBinding.runTest (package:flutter_test\src\binding.dart:638:16) #17     testWidgets.<anonymous closure>.<anonymous closure> (package:flutter_test\src\widget_tester.dart:60:24) #18     Declarer.test.<anonymous closure>.<anonymous closure> (package:test\src\backend\declarer.dart:160:19) <asynchronous suspension> #19     Invoker.waitForOutstandingCallbacks.<anonymous closure> (package:test\src\backend\invoker.dart:206:15) <asynchronous suspension> #23     Invoker.waitForOutstandingCallbacks (package:test\src\backend\invoker.dart:203:5) #24     Declarer.test.<anonymous closure> (package:test\src\backend\declarer.dart:158:29) <asynchronous suspension> #25     Invoker._onRun.<anonymous closure>.<anonymous closure>.<anonymous closure> (package:test\src\backend\invoker.dart:351:23) <asynchronous suspension> #27     StackZoneSpecification._run (package:stack_trace\src\stack_zone_specification.dart:209:15) #28     StackZoneSpecification._registerCallback.<anonymous closure> (package:stack_trace\src\stack_zone_specification.dart:119:48) #33     StackZoneSpecification._run (package:stack_trace\src\stack_zone_specification.dart:209:15) #34     StackZoneSpecification._registerCallback.<anonymous closure> (package:stack_trace\src\stack_zone_specification.dart:119:48) #39     _Timer._runTimers (dart:isolate-patch/dart:isolate/timer_impl.dart:367) #40     _Timer._handleMessage (dart:isolate-patch/dart:isolate/timer_impl.dart:401) #41     _RawReceivePortImpl._handleMessage (dart:isolate-patch/dart:isolate/isolate_patch.dart:163) (elided 17 frames from package dart:async and package dart:async-patch) 

Here is Login form widget:

import 'dart:async'; import 'dart:convert';  import 'package:app_facade/app_facade.dart'; import 'package:app_mobile/utils/dependency_injection.dart'; import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; import 'package:flutter/services.dart';  class LoginForm extends StatefulWidget {   const LoginForm({ Key key }) : super(key: key);    static GlobalKey<FormFieldState<String>> emailFieldKey = new GlobalKey<FormFieldState<String>>();   static GlobalKey<FormFieldState<String>> passwordFieldKey = new GlobalKey<FormFieldState<String>>();   static const String routeName = '/';    @override   LoginFormState createState() => new LoginFormState(); }  class LoginData {   String email = '';   String password = ''; }  class LoginFormState extends State<LoginForm> {   final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();    LoginData loginData = new LoginData();    UserApi _userApi;    void showInSnackBar(String value) {     _scaffoldKey.currentState.showSnackBar(new SnackBar(         content: new Text(value)     ));   }    bool _autovalidate = false;   bool _formWasEdited = false;   final GlobalKey<FormState> _formKey = new GlobalKey<FormState>();     @override   void initState() {     super.initState();     _userApi = new Injector().userApi;   }    Future<Null> _handleSubmitted() async {     final FormState form = _formKey.currentState;     if (!form.validate()) {       _autovalidate = true;  // Start validating on every change.       showInSnackBar('Please fix the errors in red before submitting.');     } else {       form.save();       login();     }   }    Future<Null> login() async {     try {       await _userApi.login(loginData.email, loginData.password);       Navigator.popAndPushNamed(context, '/user');     } catch (e) {       showInSnackBar(e.toString());     }   }    @override   Widget build(BuildContext context) {     return new Scaffold(       key: _scaffoldKey,       appBar: new AppBar(         title: const Text('Some'),       ),       body: new Form(           key: _formKey,           autovalidate: _autovalidate,           child: new ListView(             padding: const EdgeInsets.symmetric(horizontal: 16.0),             children: <Widget>[               new TextFormField(                 key: new Key('email'),                 decoration: const InputDecoration(                   icon: const Icon(Icons.person),                   hintText: 'Your email',                   labelText: 'Email *',                 ),                 onSaved: (String value) { loginData.email = value; },               ),               new TextFormField(                 key: LoginForm.passwordFieldKey,                 decoration: const InputDecoration(                   icon: const Icon(Icons.security),                   hintText: 'Your password',                   labelText: 'Password *',                   ),                 obscureText: true,                 onSaved: (String value) { loginData.password = value; },               ),               new Container(                 padding: const EdgeInsets.all(20.0),                 alignment: const FractionalOffset(0.5, 0.5),                 child: new RaisedButton(                   child: const Text('SUBMIT'),                   onPressed: _handleSubmitted,                 ),               ),               new Container(                 padding: const EdgeInsets.only(top: 20.0),                 child: new Text('* indicates required field', style: Theme.of(context).textTheme.caption),               ),             ],           )       ),     );   } } 

And here is widget test:

import 'package:app_facade/app_facade.dart'; import 'package:app_mobile/login_form.dart'; import 'package:app_mobile/utils/dependency_injection.dart'; import 'package:flutter_test/flutter_test.dart';  void main() {   testWidgets('login widget test', (WidgetTester tester) async {     Injector.configure(BackendType.MOCK);     // Tells the tester to build a UI based on the widget tree passed to it     var loginForm = new LoginForm();     await tester.pumpWidget(       loginForm     );      tester.enterText(find.byKey(LoginForm.emailFieldKey), "login");     tester.enterText(find.byKey(LoginForm.passwordFieldKey), "password");      var exception = tester.takeException();     print(exception);     expect(exception, equals('Sorry, only customer can login from mobile device. [Mock]'));   }); } 

I have found MediaQuery.of but don't understand how can it be used with existing widget? It accept BuildContext as parameter.

like image 420
Zufar Muhamadeev Avatar asked Jan 29 '18 10:01

Zufar Muhamadeev


People also ask

How do I fix no MediaQuery widget ancestor?

To solve this No MediaQuery widget found Error You Must have to use MaterialApp() to use MediaQuery. You cant use MediaQuery without MaterialApp Widget To use MediaQuery. of() function, first, you need to wrap your widget with MaterialApp(), even after that, you may get the error.

What is difference between scaffold and MaterialApp in flutter?

MaterialApp is a widget that introduces a number of widgets Navigator, Theme that are required to build a material design app. Scaffold Widget is used under MaterialApp, it gives you many basic functionalities, like AppBar, BottomNavigationBar, Drawer, FloatingActionButton, etc.


2 Answers

You need to wrap your widget with the MediaQuery(...) instance, and because you are using Scaffold(..) you must wrap it in a MaterialApp(..)

Read more about MediaQuery

Example:

Widget testWidget = new MediaQuery(       data: new MediaQueryData(),       child: new MaterialApp(home: new LoginForm()) ) 
like image 163
David Shuma Avatar answered Sep 23 '22 09:09

David Shuma


I had same problem and I also had to wrap it in MaterialApp, but I did that in a bit other way, without using MediaQuery. In my case it works

void main() {  Widget createWidgetForTesting({Widget child}){ return MaterialApp(   home: child, ); }  testWidgets('Login Page smoke test', (WidgetTester tester) async {  await tester.pumpWidget(createWidgetForTesting(child: new LoginPage()));  await tester.pumpAndSettle();  }); } 
like image 34
Nemanja Knežević Avatar answered Sep 22 '22 09:09

Nemanja Knežević