Flutter testing find.byValueKey() for item in drop down menu doesn't work

Viewed 2971

My test doesn't found the item in drop down menu with a value key. It's works with getText() and the value.

I create a dynamic function to fill every items with a value, a child with Text(value) and a key with Key('sign_$value_item');

This is my full form in the app:

  static const menuSigns = <String>[
    'aries',
    'taurus',
    'gemini',
    'cancer',
    'leo',
    'virgo',
    'libra',
    'scorpio',
    'sagittarius',
    'capricorn',
    'aquarius',
    'pisces'
  ];

  final List<DropdownMenuItem<String>> _dropDownMenuSigns = menuSigns
      .map<DropdownMenuItem<String>>((String value) => DropdownMenuItem<String>(
            key: new ValueKey('sign_$value_item'), // i even try with new Key('sign_$value')
            value: value,
            child: new Text(value),
          ))
      .toList();

@override
  Widget build(BuildContext context) {
    return Form(
      key: _formKey,
      child: Container(
        margin: EdgeInsets.fromLTRB(_hPad, 16.0, _hPad, 0),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.start,
          crossAxisAlignment: CrossAxisAlignment.start,
          children: <Widget>[
            Center(
              child: Container(
                padding: EdgeInsets.fromLTRB(0, 0, 0, 16.0),
                width: CustomTheme.customFormSize.width(context),
                child: DropdownButton(
                  key: Key('sign_list'),
                  isExpanded: true,
                  value: _sign,
                  style: CustomTheme.style.dropDownMenu(context),
                  hint: Text('Choose a sign'),
                  icon: Icon(Icons.arrow_drop_down_circle),
                  onChanged: ((newValue) {
                    setState(() {
                      _sign = newValue;
                    });
                  }),
                  items: _dropDownMenuSigns,
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }

And strange things, the test works with the Key if the length of value is very long, for exemple more 10 characters.

This is my test:

import 'package:flutter_driver/flutter_driver.dart';
import 'package:flutter_gherkin/flutter_gherkin.dart';
import 'package:gherkin/gherkin.dart';

class AstroSignValidation extends AndWithWorld<FlutterWorld> {
  @override
  Future<void> executeStep() async {
    await FlutterDriverUtils.getText(world.driver, find.text('AstroDay'));
    await FlutterDriverUtils.tap(world.driver, find.byValueKey('sign_list')); // open drop down menu is ok
    await FlutterDriverUtils.tap(world.driver, find.byValueKey('sign_cancer_item')); // here test not passed
  }

  RegExp get pattern => RegExp(r"I expect the user enters sign");
}

Edit: This is my feature file :

Feature: Get Astro day
  User should be able to get successfully his astro after cliking astro button.

  Scenario: User get astro in successfully
    Given I expect the "user" 1 sign
    And I expect the user enters day
    When user hits Show your astro button
    Then user should land on result screen
2 Answers

I recreated your case. Instead of using key property on DropdownMenuItem, you need to use it inside it's child, ie, in Text widget. That way, since the flutter driver will look for text to be selected when dropdown menu is open, the key property will come into play when menu items are displayed and then easier to click on whatever option we pass in the test. It worked well. Updated working code below:

final List<DropdownMenuItem<String>> _dropDownMenuSigns = menuSigns
      .map<DropdownMenuItem<String>>((String value) => DropdownMenuItem<String>(
  //  key: new ValueKey('sign_$value'),
    value: value,
    child: new Text(value, key: Key('sign_$value'),),  // use key here on text
  ))
      .toList();

driver test:

class AstroSignValidation extends GivenWithWorld<FlutterWorld> {
  @override
  Future<void> executeStep() async {
    await FlutterDriverUtils.getText(world.driver, find.text('Choose a sign'));
    await FlutterDriverUtils.tap(world.driver, find.byValueKey('sign_list')); // open drop down menu is ok
    await FlutterDriverUtils.tap(world.driver, find.byValueKey('sign_virgo')); // selects sign properly
    print('selected sign');
  }

  RegExp get pattern => RegExp(r"I expect the user enters sign");
}

And test passes :

enter image description here

Note: I directly used Given statement in feature file and accordingly extended GivenWithWorld class in my test. You'll need to use it per your needs.

Hope this answers your question.

Related