Flutter小部件测试无法触发DropdownButton在选择另一个项目时的onChanged事件

7

我正在编写一个Flutter Web应用程序,并在代码库中添加一些小部件测试。我有困难使flutter_test按预期工作。我目前面临的问题是尝试在DropdownButton中选择值。

以下是完整的小部件测试代码,可重现该问题:

void main() {
  group('description', () {
    testWidgets('description', (WidgetTester tester) async {
      await tester.pumpWidget(MaterialApp(
        home: Card(
          child: Column(
            children: [
              Expanded(
                child: DropdownButton(
                  key: Key('LEVEL'),
                  items: [
                    DropdownMenuItem<String>(
                      key: Key('Greater'),
                      value: 'Greater',
                      child: Text('Greater'),
                    ),
                    DropdownMenuItem<String>(
                      key: Key('Lesser'),
                      value: 'Lesser',
                      child: Text('Lesser'),
                    ),
                  ],
                  onChanged: (value) {
                    print('$value');
                  },
                  value: 'Lesser',
                ),
              )
            ],
          ),
        ),
      ));

      expect((tester.widget(find.byKey(Key('LEVEL'))) as DropdownButton).value,
          equals('Lesser'));

      await tester.tap(find.byKey(Key('LEVEL')));

      await tester.tap(find.byKey(Key('Greater')));
      await tester.pumpAndSettle();

      expect((tester.widget(find.byKey(Key('LEVEL'))) as DropdownButton).value,
          equals('Greater'));
    });
  });
}

这个测试在最后一个期望值处失败 -- expect(widget.value, equals('Greater'));

根据我的调试器或输出中的打印语句,onChanged回调从未被调用。

如何测试DropdownButton的行为呢?

1个回答

10

在测试中,重要的是在任何与用户交互相关的代码之后添加 tester.pump() 调用。

下拉按钮的测试与常规小部件略有不同。对于所有情况,最好参考此处,这是下拉按钮的实际测试。

测试时的一些提示:

  1. DropdownButton由一个“IndexedStack”和一个普通stack组成,用于菜单项的列表。
  2. 以某种方式,你为DropDownMenuItem分配的键和文本会分别应用到上述堆栈中的两个窗口小部件。
  3. 在点击时,请选择返回的小部件列表中的最后一个元素。
  4. 另外,下拉按钮需要一些时间进行动画处理,因此按照 flutter 中所引用的测试建议,我们调用 tester.pump 两次。
  5. DropdownButtonvalue属性不会自动更改。它必须使用setState设置。因此,除非将测试包装在类似于此处StatefulBuilder中,否则你的最后一行断言是错误的并且无法正常工作。

有关如何使用DropDownButton的更多详细信息,请查看此贴子

      
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';

void main() {
  group('description', () {
    testWidgets('description', (WidgetTester tester) async {
      String changedValue = 'Lesser';
      await tester.pumpWidget(MaterialApp(
        home: Scaffold(
          body: Center(
            child: RepaintBoundary(
              child: Card(
                child: Column(
                  children: [
                    Expanded(
                      child: DropdownButton(
                        key: Key('LEVEL'),
                        items: [
                          DropdownMenuItem<String>(
                            key: ValueKey<String>('Greater'),
                            value: 'Greater',
                            child: Text('Greater'),
                          ),
                          DropdownMenuItem<String>(
                            key: Key('Lesser'),
                            value: 'Lesser',
                            child: Text('Lesser'),
                          ),
                        ],
                        onChanged: (value) {
                          print('$value');
                          changedValue = value;
                        },
                        value: 'Lesser',
                      ),
                    )
                  ],
                ),
              ),
            ),
          ),
        ),
      ));

      expect((tester.widget(find.byKey(Key('LEVEL'))) as DropdownButton).value,
          equals('Lesser'));
      // Here before the menu is open we have one widget with text 'Lesser'
      await tester.tap(find.text('Lesser'));
      // Calling pump twice once comple the the action and
      // again to finish the animation of closing the menu.
      await tester.pump();
      await tester.pump(Duration(seconds: 1));

      // after opening the menu we have two widgets with text 'Greater'
      // one in index stack of the dropdown button and one in the menu .
      // apparently the last one is from the menu.
      await tester.tap(find.text('Greater').last);
      await tester.pump();
      await tester.pump(Duration(seconds: 1));

      /// We directly verify the value updated in the onchaged function.
      expect(changedValue, 'Greater');

      /// The follwing expectation is wrong because you haven't updated the value
      ///  of dropdown button.
      // expect((tester.widget(find.byKey(Key('LEVEL'))) as DropdownButton).value,
      //     equals('Greater'));
      
    });
  });
}


2
我已经编辑了上面的代码,以展示我已经添加了对tester.pump()的调用,但是测试仍然失败,因为DropdownButton的值仍然是“Lesser”。 - beer geek
你使用的Flutter版本是哪个?你能否发布Flutter Doctor的摘要,这样我就可以尝试复制。在我的情况下,它失败了,因为某种原因“finder”找到了多个具有相同键的小部件。 - Abhilash Chandran

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接