Skip to content

Default finders from flutter_test don't work with FaIcon #198

Open
@bartekpacia

Description

@bartekpacia

The problem

Let's say that I have the following Widget:

IconButton(
    iconSize: 20,
    icon: FaIcon(FontAwesomeIcons.eye),
    onPressed: () {/* change PasswordTextField visibility */},
  );

If I want to test that this Widget is visible, I'd normally use

// password_text_field_test.dart

 testWidgets(
      'tapping on password visibility button toggles it',
      (tester) async {
        final eyeFinder = find.widgetWithIcon(IconButton, FontAwesomeIcons.eye);
        expect(eyeFinder, findsOneWidget);
});

But the above will fail:

tapping on password visibility button toggles it
══╡ EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK ╞════════════════════════════════════════════════════
The following TestFailure was thrown running a test:
Expected: exactly one matching node in the widget tree
  Actual: _AncestorFinder:<zero widgets with type "IconButton" which is an ancestor of icon
"IconData(U+0F06E)">
   Which: means none were found but one was expected

The reason is that FaIcon is not an Icon (i.e FaIcon extends StatelessWidget), so find.widgetWithIcon can't find it.

The solution 1/2

To make find.widgetWithIcon work with FaIcon, we have to create a custom implementation of Flutter's WidgetIconFinder:

class WidgetFaIconFinder extends MatchFinder {
  WidgetFaIconFinder(this.icon, {bool skipOffstage = true})
      : super(skipOffstage: skipOffstage);

  final IconData icon;

  @override
  String get description => 'Font Awesome icon "$icon"';

  @override
  bool matches(Element candidate) {
    final widget = candidate.widget;
    return widget is FaIcon && widget.icon == icon;
  }
}

Then, to make it shorter for use in tests, we can create our own CustomFinders class, basing on Flutter's CommonFinders:

// finders.dart

const customFind = CustomFinders._();

class CustomFinders {
  const CustomFinders._();

  /// Like [CommonFinders.byIcon], but for FontAwesomeIcons.
  Finder byFaIcon(IconData icon, {bool skipOffstage = true}) =>
      WidgetFaIconFinder(icon, skipOffstage: skipOffstage);

  /// Like [CommonFinders.widgetWithIcon], but for FontAwesomeIcons.
  Finder widgetWithFaIcon(
    Type widgetType,
    IconData icon, {
    bool skipOffstage = true,
  }) {
    return find.ancestor(
      of: customFind.byFaIcon(icon),
      matching: find.byType(widgetType),
    );
  }
}

The solution 2/2

Then, we can use our custom finders just like we'd use the default ones

// password_text_field_test.dart

 testWidgets(
      'tapping on password visibility button toggles it',
      (tester) async {
        final eyeFinder = customFind.widgetWithFaIcon(IconButton, FontAwesomeIcons.eye);
        expect(eyeFinder, findsOneWidget);
});

Further steps

I see 2 ways:

  • add custom finders to this package, so users don't have to write their own
  • include information about how to test FaIcon (maybe link to this issue?)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions