flutterdartdropdownbuttonwidget-test-flutter

Widget Test Doesn't Fire DropdownButton onChanged


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.