7

i try to unittest a small own widget:

testWidgets('MyWidget has a title and message', (WidgetTester tester) async {
  var text = "abc";
  var label = "def";

  await tester.pumpWidget(LabeledTextWidget(
    text,
    label: label,
  ));

  final textFinder = find.text(text);
  final labelFinder = find.text(label);

  expect(textFinder , findsOneWidget);
  expect(labelFinder, findsOneWidget);
});

Here is the widget code:

class LabeledTextWidget extends StatelessWidget {
  final String text;
  final String label;
  LabeledTextWidget(this.text, {this.label});

  @override
  Widget build(BuildContext context) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.stretch,
        children: <Widget>[
         Text(label,style: Theme.of(context).textTheme.caption,),
         Text(text),
         ],
    );
  }

The test always throws the exception:

...
No Directionality widget found.
RichText widgets require a Directionality widget ancestor.
The specific widget that could not find a Directionality ancestor was:
RichText
...

I can avoid this error by adding a text direction to all Text-Widgets. (textDirection: TextDirection.ltr,), but i thats a bad solution and it doesn´t work with rows.

2 Answers 2

8

Ok, here is the solution.

You have to wrap the Widget with this:

Directionality(
  child: MediaQuery(
    data: MediaQueryData(),
    child: LabeledTextWidget(this.text, {this.label}),
  ),
  textDirection: TextDirection.ltr,
);
Sign up to request clarification or add additional context in comments.

Comments

3

In a real world Flutter app be preapred that the Widget under test might depend on a whole bunch of framework widgets that should preceed it and you can face numerous errors:

No Directionality widget found, No MaterialLocalizations found, No material widget, No Overlay, etc.

The best way to go is to digest the main.dart entry point and extract the bare minimum (via trial and error). Here's my pumpWidget example were I create and wrap the target OnlineDictionaries widget:

  await tester.pumpWidget(
    MultiProvider(
      providers: [
        ChangeNotifierProvider<OnlineDictionaryManager>(
          create: (context) => OnlineDictionaryManager(FakeOnlineRepo()),
        )
      ],
      child: MaterialApp(
        localizationsDelegates: [
          GlobalMaterialLocalizations.delegate,
          GlobalWidgetsLocalizations.delegate,
          GlobalCupertinoLocalizations.delegate,
        ],
        supportedLocales: [
          const Locale('en', ''),
          const Locale('be', ''),
          const Locale('ru', ''),
        ],
        initialRoute: '/',
        routes: {'/': (context) => Scaffold(body: OnlineDictionaries())},
      ),
    ),
  );

Note that the OnlineDictionaries widget relies on Providers for state management, it uses localizations and the app uses Overlays/Navigator (addressed by initialRoute and routes properties).

Afterwards you can manipulate the widget (e.g. checking if load indicator is displayed and if an error message is shown if TextFormField is cleared):

  await tester
      .pump(Duration(milliseconds: 10)); // let progress indicator appear

  expect(find.byType(LinearProgressIndicator), findsOneWidget);

  await tester.pumpAndSettle();

  var field = find.byType(TextFormField);

  expect(field, findsOneWidget);

  await tester.enterText(find.byType(TextFormField), '');

  await tester.pumpAndSettle(Duration(milliseconds: 100));

  final errorFinder = find.text('URL can\'t be empty');

  expect(errorFinder, findsOneWidget);

Here's a complete example (also includes Mockito and mocking SharedPreferences): https://github.com/maxim-saplin/dikt/blob/master/test/ui_dictionaries_test.dart

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.