Widget Test Doesn't Fire DropdownButton onChanged

Issue

I have a widget test that taps an item in the DropdownButton. That should fire the onChanged callback but it doesn’t. Here is the test code. The mock is Mockito.


void main() {
  //Use a dummy instead of the fake. The fake does too much stuff
  final mockServiceClient = MockTheServiceClient();

  final apiClient = GrpcApiClient(client: mockServiceClient);

  when(mockServiceClient.logEvent(any))
      .thenAnswer((_) => MockResponseFuture(LogEventResponse()));

  testWidgets("select asset type", (tester) async {
    //sets the screen size
    tester.binding.window.physicalSizeTestValue = const Size(3840, 2160);

    // resets the screen to its orinal size after the test end
    addTearDown(tester.binding.window.clearPhysicalSizeTestValue);

    await tester.pumpWidget(AssetApp(apiClient), const Duration(seconds: 5));

    //Construct key with '{DDLKey}_{Id}'
    await tester
        .tap(find.byKey(ValueKey("${assetTypeDropDownKey.value}_PUMP")));

    await tester.pumpAndSettle(const Duration(seconds: 5));

    verify(mockServiceClient.logEvent(any)).called(1);
  });
}

This is the build method of the widget:

  @override
  Widget build(BuildContext context) {
    return DropdownButton<DropDownItemDefinition>(
      underline: Container(),
      dropdownColor: Theme.of(context).cardColor,
      hint: Text(
        hintText,
        style: Theme.of(context).textTheme.button,
      ),
      //TODO: Use the theme here
      icon: Icon(
        Icons.arrow_drop_down,
        color: Theme.of(context).dividerColor,
      ),
      value: getValue(),
      onChanged: (ddd) {
        setState(() {
          onValueChanged(ddd!);
        });
      },
      items: itemss.map<DropdownMenuItem<DropDownItemDefinition>>((value) {
        return DropdownMenuItem<DropDownItemDefinition>(
          key: ValueKey(
              "${(key is ValueKey) ? (key as ValueKey?)?.value.toString() :
               ''}_${value.id}"),
          value: value,
          child: Tooltip(
              message: value.toolTipText,
              child: Container(
                  margin: dropdownPadding,
                  child: Text(value.displayText,
                      style: Theme.of(context).textTheme.headline3))),
        );
      }).toList(),
    );
  }

Note that the onValueChanged function calls the logEvent call. The onChanged callback never happens and the test fails. This is the code it should fire.

  Future onAssetTypeChange(DropDownItemDefinition newValue) async {
    await assetApiClient.logChange(record.id, newValue, DateTime.now());
  }

Why does the callback never fire?

Note: I made another widget test and the Mock does verify that the client was called correctly. I think there is some issue with the callback as part of the widget test.

Solution

You need to first instruct the driver to tap on the DropdownButton itself, and then, after the dropdown popup shows up, tap on the DropdownMenuItem.

The driver can’t find a DropdownMenuItem from the dropdown if the dropdown itself is not active/painted on the screen.

Answered By – Adrian Flutur

Answer Checked By – Gilberto Lyons (FlutterFixes Admin)

Leave a Reply

Your email address will not be published. Required fields are marked *