我如何测试实际发生的转换

how do I test for a transition actually occuring

给定一个在 Stream 事件中“摇晃”的小部件

class Shaker extends StatefulWidget {
  final Widget child;
  final Stream<void> stream;
  final Duration duration;
  final int loops;
  const Shaker({
    required this.child,
    required this.stream,
    this.duration = const Duration(milliseconds: 100),
    this.loops = 2,
    Key? key,
  }) : super(key: key);

  @override
  State<Shaker> createState() => _ShakerState();
}

class _ShakerState extends State<Shaker> with SingleTickerProviderStateMixin {
  late final AnimationController _controller;
  late final StreamSubscription _subscription;
  late final Animation<Offset> _offsetAnimation;

  @override
  void initState() {
    _controller = AnimationController(
      vsync: this,
      duration: widget.duration,
    );

    _subscription = widget.stream.listen((event) async {
      for (var i = 0; i < widget.loops; ++i) {
        await _controller.forward();
        await _controller.reverse();
      }
    });

    _offsetAnimation = Tween<Offset>(
      begin: Offset.zero,
      end: const Offset(.02, 0.0),
    ).animate(CurvedAnimation(
          parent: _controller,
          curve: Curves.elasticIn,
        ));
    super.initState();
  }

  @override
  void dispose() {
    _controller.dispose();
    _subscription.cancel();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return SlideTransition(
      position: _offsetAnimation,
      child: widget.child,
    );
  }
}

dartpad example

什么是测试事件接收到的“抖动”实际发生的优雅方法?
谢谢

在您的小部件测试中,您可以使用 WidgetTester.pump()WidgetTester.pumpAndSettle() 来验证此行为。

pump() 只是从框架渲染一个新帧,但是 pumpAndSettle() 连续渲染帧直到没有更多的预定帧,然后 returning 预定的帧数.

一个简单(但不太精确)的测试是确保您的动画播放正确的帧数。类似于:

const expectedFramesForShake = 10;

testWidgets('example', (tester) async {
  final controller = StreamController<void>();
  await tester.pumpWidget(Shaker(stream: controller.stream, /* other fields */));
  
  controller.add(null);  // cause a shake
  final frames = await tester.pumpAndSettle();
  expect(frames, expectedFramesForShake); 
});

请注意,您可能必须将小部件包装在 MaterialApp 中以设置必要的 InheritedWidget 以通过测试。

这验证了动画播放的时间大致正确,但我们可以做得更好!

与其依赖 pumpAndSettle() 到 return 计划的帧数,我们可以在每一帧之后检查小部件树。

// change this so it matches the values produced by the "correct" animation
const expectedOffsets = [0.0, 1.0, 3.0, 7.0,]; 

testWidgets('better example', (tester) async {
  final controller = StreamController<void>();
  await tester.pumpWidget(Shaker(stream: controller.stream, /* other fields */));

  final offsets = <double>[];  // empty list to store results

  controller.add(null);
  do {
    // pull the SlideTransition from the widget tree
    final transition = tester.widget<SlideTransition>(find.byType<SlideTransition>);

    // read the current value of the position animation
    final horizontalOffset = transition.position.value.dx;
    offsets.add(horizontalOffset);

    // now request a new frame
    await tester.pump();

    // only loop while there are more frames scheduled
  } while (tester.binding.hasScheduledFrame);

  // compare the 2 arrays
  expect(offsets, expectedOffsets);
});

这使您可以确定 Shaker 正在为每个帧偏移其子项的正确量。但是,这会使您的测试非常脆弱,例如,如果您想更改摇晃的持续时间,它将更改 offsets.

的值

P.S。感谢 dartpad 示例 :-)