ndequeker
ndequeker

Reputation: 7990

How to test the order of multiple list items in Flutter / Dart?

Intro

We have a ListView named OrganizationList which contains a ListTile widget for every item. We use a Text widget to display the name of the organization inside the ListTile.

We would like to test if the organization name is displayed correctly and in the correct order.

Our current solution

We have the following assertions:

var organizationA = find
    .descendant(
        of: find.byType(OrganizationList),
        matching: find.byKey(
            Key('0'),
        ),
     )
     .evaluate()
     .first
     .widget as Text;
expect(textOrganization.data, 'organization-a');

var organizationB = find
    .descendant(
        of: find.byType(OrganizationList),
        matching: find.byKey(
            Key('1'),
        ),
     )
     .evaluate()
     .first
     .widget as Text;
expect(textOrganization.data, 'organization-b');

This feels like a very cumbersome way of testing if the right label is shown for the list items. But I fail to find a more elegant way.

Question

What is a more elegant way in Flutter / Dart to assert both the content of a list item and the order of all items?

Upvotes: 6

Views: 5566

Answers (3)

Ahmed Sameh
Ahmed Sameh

Reputation: 1

Try a for loop:

// This is for saving a reference to the ListTile from the previous iteration
ListTile? previousListTile;

for (int j = 0; j < aListOfModels.length; j++) {
  final organizationA = find.descendant(
      of: find.byType(OrganizationList),
      matching: find.byKey(
          Key('0'),
      ),
   );
  // This is important to show the invisible late elements
  await widgetTester.scrollUntilVisible(organizationA, tryToFindTheSuitableDelta);

  // I suppose that organizationA finds a ListTile widget
  ListTile currentListTile = widgetTester.widget<ListTile>(organizationA);
  /* 
  Make your expectations about the content as you like
  ....
  ...
  */

  // To expect about the order, do the following:
  final listTileWidgetList = widgetTester
      .widgetList<ListTile>(find.byType(ListTile))
      .toList();
  
  if (j > 0) {
    // Expect that the currentListTile is the first next ListTile of the previousListTile
    expect(listTileWidgetList.indexOf(currentListTile),
      equals(listTileWidgetList.indexOf(previousListTile!) + 1));
  }
  // To allow you to make your expectation about the order in the next iteration
  previousListTile = currentListTile;
}

Upvotes: 0

Jitesh Mohite
Jitesh Mohite

Reputation: 34220

The easiest way to get elements from List and verify is widgetList, which gives a list of elements of whatever data type we have provided.

Sample test:

Problem: Verify the second item from List

const List<String> listString = [
  "Ac",
  "Fuel Sensor",
  "Power",
  "Panic",
  "Camera",
  "Relay",
  "Duty Button",
  "Other"
];

Test:

testWidgets('Listview item verify', (WidgetTester tester) async {
      await tester.pumpWidget(ListViewApp());
     /// Verify OrganizationIte sequence to get and verify title. 
      OrganizationItem item = tester
          .widgetList<OrganizationItem>(find.byType(OrganizationItem))
          .elementAt(2);
      String data = item.title;
      expect("Power", data);
    });

App Code:

 class ListViewApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Checked Listview',
      theme: ThemeData(
        primarySwatch: Colors.green,
      ),
      home: MyHomePage(title: 'Flutter Checked Listview'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key? key, required this.title}) : super(key: key);
  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  List<String> _list = [];

  @override
  void initState() {
    setState(() {
      for (int i = 0; i < listString.length; i++) {
        _list.add('${listString[i]}');
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Listview Sample'),
      ),
      body: ListView.builder(
        itemBuilder: (_, int index) {
          return OrganizationItem(
            title: _list[index],
          );
        },
        itemCount: _list.length,
      ),
    );
  }
}

class OrganizationItem extends StatelessWidget {
  final String title;

  const OrganizationItem({Key? key, required this.title}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return ListTile(
      title: Text(this.title),
    );
  }
}

Upvotes: 5

Dylar
Dylar

Reputation: 31

With my solution you can check if a ListItemWidget at specific position and text is visible(or not):

void checkPosition(int index, SomeObj obj, {bool isOnPosition = true}) {
  final listItemFinder = find.byType(ListItemWidget);
  final listItem = listItemFinder.evaluate().isEmpty
      ? null
      : listItemFinder.at(index);
  if (listItem == null) {
    if (isOnPosition) {
      fail('List not found');
    }
    return;
  }
  final positionText = find.text(obj.text);
  expect(find.descendant(of: listItem, matching: positionText),
      isOnPosition ? findsOneWidget : findsNothing);
}

Upvotes: 3

Related Questions