【问题标题】:Custom widget testing needs Material widget to test自定义小部件测试需要 Material 小部件来测试
【发布时间】:2021-05-24 12:35:00
【问题描述】:

我正在尝试在 Flutter 中执行基本的小部件测试。基本上我想要一个包含数据列表的列表,并在其中也有一个 ListTile 小部件的自定义小部件 (BasicListItem) 中显示每个项目。

根小部件:

class MyApp extends StatelessWidget {
  final List taskList = ['List-1', 'List-2', 'List-3'];

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      home: Scaffold(
        body: ListView.builder(
            itemCount: taskList.length, itemBuilder: _itemBuilder),
      ),
    );
  }

  Widget _itemBuilder(BuildContext context, int index) {
    final String item = taskList[index];
    return BasicListItem(key: Key(item), title: item);
  }
}

列表项小部件 (BasicListItem) 采用标题,并在 ListTile 小部件中使用它。

class BasicListItem extends StatelessWidget {
  final String title;

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

  @override
  Widget build(BuildContext context) {
    return ListTile(
      leading: Icon(Icons.map),
      title: Text(title),
    );
  }
}

这是对它的测试:

testWidgets('has title and Icons', (WidgetTester tester) async {

  const testKey = Key('my-key-1');
  const testTitle = 'Demo title';

  await tester.pumpWidget(BasicListItem(key: testKey, title: testTitle));

  expect(find.text(testTitle), findsOneWidget);
});

但是测试抛出错误:

未找到材质小部件。 ListTile 小部件需要一个 Material 小部件 祖先。

... ...

在运行测试时抛出了以下 TestFailure 对象:
预期:小部件树中只有一个匹配节点 实际: _TextFinder:

但是,如果我在 BasicListItem 构建方法中将 ListTile 包裹在 MaterialApp 周围,则测试确实通过。像这样:

@override
Widget build(BuildContext context) {
  return MaterialApp(
    title: title,
    home: Scaffold(
      body: ListTile(
        leading: Icon(Icons.map),
        title: Text(title),
      ),
    )
  );
}

但是这样做我不能在 ListView 小部件中使用它。而且我还想拥有模块化/单独的自定义小部件,以便我也可以在不同的地方使用它。我是新手,也许我错过了一些东西。如何构建自定义小部件并对其进行测试?请你帮帮我。

【问题讨论】:

    标签: flutter flutter-test


    【解决方案1】:

    ListTile 组件来自 Flutter UI 组件的Material 部分,不是一个独立的小部件,因此它需要一个MaterialApp 作为父级。

    您可以在此处检查ListTile 是否在材料库下:https://api.flutter.dev/flutter/material/ListTile-class.html

    此外,您可以创建尽可能多的自定义 Widgets 以在单独的模块中使用,
    唯一的要求是在应用初始化的最开始使用MaterialApp

    import 'package:flutter/material.dart';
    
    void main() => runApp(MyApp());
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        /// Only this needs to be a MaterialApp
        return MaterialApp(
          title: 'Welcome to Flutter',
    
          /// this point to different screen widget also, like MainScreen()
          /// Or you can start using Scaffold from here as well.
          home: Scaffold(
            appBar: AppBar(
              title: Text('Welcome to Flutter'),
            ),
            body: Center(
              child: Text('Hello World'),
            ),
          ),
        );
      }
    }
    

    没有必要在您构建的每个自定义小部件上都使用 MaterialApp 作为父级。只是根也可以。
    但是,如果您使用单个小部件进行简单测试,并且它需要 Material 祖先,您也可以简单地将小部件包装在 Material 小部件中。

    【讨论】:

    • 如果我将 MaterialApp 与 ListTile 一起使用,我如何将它与不同的小部件一起使用?如果我将 BasicListItem 与 ListView 一起使用,则会引发错误。另外,我的印象是 MaterialApp 必须是小部件的根。我错过了什么?
    • YesMaterialApp 必须是 Flutter 应用程序的 root 以便您不必在任何地方声明 MaterialApp 并且您可以利用 @ 等 Material 属性987654334@ & theming.
    【解决方案2】:

    一开始我没看懂Darshan的回答,因为我觉得他提供的代码让我直接将MaterialApp和Material小部件实现到BasicListItem小部件类build方法中,而不是仅仅在测试服。但这给了我实施它的线索。

    所以,这是最终的测试用例。我确实用 BasicListItem 包装了 MaterialApp 和 Material 小部件,但不是在 build 方法中,而是我将它们包装在测试用例上:

    testWidgets('has title and Icons', (WidgetTester tester) async {
    
      const testKey = Key('my-key-1');
      const testTitle = 'Demo title';
    
      await await tester.pumpWidget(MaterialApp(
        home: Material(
          child: BasicListItem(key: testKey, title: testTitle),
        ),
      ));;
    
      expect(find.text(testTitle), findsOneWidget);
    });
    

    我希望这也能帮助像我这样的人。

    【讨论】:

      【解决方案3】:

      好的,这并不是 Flutter Docs 中专门提到的,而是到处都有暗示。在颤振测试方面,我们正在抽取一个根小部件来渲染一个框架,作为我们用于测试小部件的调色板。

      转换为您需要创建一个 Root App Widget 来包装被测小部件。 eBay 的 Golden Toolkit 通过 pumpWidgetBuilder 提供了钩子,使之成为可能,它是 Widget Tester 的扩展。

      更多信息请看我的博客,https://fredgrott.medium.com

      【讨论】:

        猜你喜欢
        • 2019-05-23
        • 2019-03-26
        • 2021-05-09
        • 2019-02-25
        • 2019-12-27
        • 2016-07-26
        • 2020-11-23
        • 2021-09-22
        • 2019-06-13
        相关资源
        最近更新 更多