如何在Flutter中测试在didUpdateWidget()中实例化的小部件?

6

我有一个StatefulWidget,它在didUpdateWidget()中创建了一个AnimatedCrossFade小部件,并将其保存为animation。以下是一个简化的示例:

class BackgroundImage extends StatefulWidget {
  final Color colorA;
  final Color colorB;

  const BackgroundImage({
      this.colorA,
      this.colorB,
  });
}

class _BackgroundImageState extends State<BackgroundImage> {
  Widget _animation;

  @override
  void didUpdateWidget(Widget old) {
    super.didUpdateWidget(old);
    _buildBackgroundA(colorA).then((backgroundA) {
      _buildBackgroundB(colorB).then(backgroundB) {
        print(backgroundA); // this is not null
        print(backgroundB); // this is not null
        _animation = AnimatedCrossFade(
          duration: Duration(seconds: 15),
          firstChild: backgroundA,
          secondChild: backgroundB,
          crossFadeState: someVarToSwitchColors,
              ? CrossFadeState.showFirst
              : CrossFadeState.showSecond,
          );
       }
     }
  }

  @override
  Widget build(BuildContext context) {
    return _animation != null ? _animation : Container();
  }
}

_buildBackgroundA()_buildBackgroundB() 是异步函数,返回Future<Widget>。这在我的应用程序中可以正常工作 - didUpdateWidget() 被调用,我的 AnimatedCrossFade 出现并在两个背景之间进行动画。

然而,在我的测试中,我无法找到AnimatedCrossFade。我能够找到其他的无状态小部件,也能够找到 BackgroundImage 小部件。我的代码类似于:

    await tester.pump();
    await tester.pumpAndSettle(Duration(minutes: 1));
    expect(
        find.descendant(
            of: find.byType(BackgroundImage),
            matching: find.byType(AnimatedCrossFade)),
        findsOneWidget);

这个失败了,因为找不到 AnimatedCrossFade

如果我把我的 build() 函数改成:

  @override
  Widget build(BuildContext context) {
    return AnimatedCrossFade(...);
  }

我能够找到我的小部件。 因此,我怀疑这与我的测试 expect_buildBackground() 函数完成之前执行有关。 我已尝试更改 pumppumpAndSettle 中的持续时间,但没有效果。 我该如何强制测试等待更长时间? 还有其他我漏掉的东西吗?

测试日志如下(包括我的打印输出):

Running Flutter tests.
00:00 +0: ...d/work/.../cl00:00 +0: (setUpAll)                                                                                                          00:00 +0: Test background                                              
init state called
_buildBackgroundA called
init state called
_buildBackgroundB called
...
00:00 +0 -1: Test background
    Expected: exactly one matching node in the widget tree
    Actual: ?:<zero widgets with type "AnimatedCrossFade" that has ancestor(s) with type "BackgroundImage" (ignoring offstage widgets)>
     Which: means none were found but one was expected
  This was caught by the test expectation on the following line:
 ...line 179

about to return from calling _buildBackgroundA
image: Image(image: MemoryImage(_Uint8ArrayView#af55b, scale: 1.0), width: 50.0, height: 200.0, fit: cover, alignment: center, this.excludeFromSemantics: false, filterQuality: low)
about to return from calling _buildBackgroundB
image: Image(image: MemoryImage(_Uint8ArrayView#79291, scale: 1.0), width: 50.0, height: 830.0, fit: cover, alignment: center, this.excludeFromSemantics: false, filterQuality: low)
...

1个回答

1

我们不需要将didUpdateWidget作为一个单独的方法进行测试。因此,我们首先需要知道在flutter中什么时候调用didUpdateWidget

  1. 当widget的父级widget重建时:如果某个widget的父级widget由于某些更改而重建,则框架也会重建该widget。这将触发使用新widget调用didUpdateWidget方法。

  2. 当具有新参数的StatefulWidget被重新构建时:如果StatefulWidget使用新参数被重建,则框架将调用didUpdateWidget方法。这是因为widget已经更改,它可能需要更新其内部状态以反映新参数。

  3. 当使用相同参数重新构建StatefulWidget时:如果StatefulWidget使用相同的参数重新构建,则框架将调用didUpdateWidget方法。这是因为widget已经更改,即使参数相同,它也可能需要更新其内部状态。

所以,如果你在测试一个没有父级的小部件时。用StatefulBuilder将其包裹起来,并通过任何方式更改传递给小部件的变量,以测试didUpdateWidget是否正常工作。

我会举个例子来让你了解如何在你的代码中实现。

      await tester.pumpWidget(
        MaterialApp(
          theme: AppThemes.theme(themeMode: ThemeMode.light),
          home: Scaffold(
            body: StatefulBuilder(
              builder: (context,setState) {
                return GiftWrap(
                  title: titleString,
                  subTitle: "You Have 0 Loyalty Point.",
                  icon: Icons.info_outline,
                  iconLabel: "Redeem Now",
                  iconPath: loyalPointIconPath,
                  onTap: (redeem) {
                    setState((){
                      titleString = "Loyalty";
                    });
                  },
                );
              }
            ),
          ),
        ),
      );

如果您还有任何疑问,请在下方评论。


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