如何在 Flutter 小部件测试中组合两个 Finder?

Joh*_*yan 6 flutter flutter-integration-test

使用package:flutter_test,我可以创建一个查找器,通过键查找小部件:

expect(find.byKey(const ValueKey('counter')), findsOneWidget);
Run Code Online (Sandbox Code Playgroud)

或通过文字:

expect(find.text('0'), findsOneWidget);
Run Code Online (Sandbox Code Playgroud)

我还可以找到从此小部件衍生的小部件:

expect(
  find.descendant(
    of: find.byKey(const ValueKey('counter')),
    matching: find.text('0'),
  ),
  findsNothing,
);
Run Code Online (Sandbox Code Playgroud)

或者祖先:

expect(
  find.ancestor(
    of: find.text('0'),
    matching: find.byKey(const ValueKey('counter')),
  ),
  findsNothing,
);
Run Code Online (Sandbox Code Playgroud)

但是我如何组合这些查找器来验证是否存在带有“计数器”键且文本为“0”的小部件?例如:

Text(
  '$_counter',
  key: const Key('counter'),
  style: Theme.of(context).textTheme.headline4,
),
Run Code Online (Sandbox Code Playgroud)

pdb*_*asi 4

我在尝试自己组合查找器时发现了这个问题。我最终找到了如何组合查找器,但在查找时,我还找到了我认为对您的具体案例更好的答案。在这里分享它们。

问题 1:如何使用查找器验证小部件的属性?

由于您使用的是键,我建议将“查找小部件”和“验证小部件上的属性”的想法分开。

因此,您可以使用该WidgetController.widget<T>方法来获取小部件,然后使用expect. 在你的情况下,它看起来像这样:

expect(
  tester.widget<Text>(find.byKey(const ValueKey('counter'))).data,
  equals('0'),
);
Run Code Online (Sandbox Code Playgroud)

问题2:如何组合查找器?

这是您所问的更普遍的问题,所以我想我也应该分享我的发现。

这是我使用扩展方法和ChainedFinder抽象类得出的结论。

它创建了一个find.chained方法,该方法采用查找器列表并使用该类将它们链接在一起ChainedFinderLink。它确保列表中的查找器按照给定的顺序应用,并根据每个应用程序进一步过滤候选列表。

import 'dart:collection';

import 'package:darq/darq.dart';
import 'package:flutter/src/widgets/framework.dart';
import 'package:flutter_test/flutter_test.dart';

extension CommonFinderX on CommonFinders {
  Finder chained(List<Finder> finders) {
    assert(finders.isNotEmpty);

    final findersQueue = Queue<Finder>.from(finders);
    var current = findersQueue.removeFirst();
    while (findersQueue.isNotEmpty) {
      current = ChainedFinderLink(
        parent: current,
        finder: findersQueue.removeFirst(),
      );
    }

    return current;
  }
}

class ChainedFinderLink extends ChainedFinder {
  ChainedFinderLink({
    required Finder parent,
    required this.finder,
  }) : super(parent);

  final Finder finder;

  @override
  String get description => '${parent.description} THEN ${finder.description}';

  @override
  Iterable<Element> filter(Iterable<Element> parentCandidates) {
    /// We have to apply against the interection of `parentCandidates` and
    /// `finder.allCandidates` because some finders (such as ancestor) filter
    /// out invalid candidates through the `allCandidates` getter instead of
    /// as part of the `apply` method itself.
    return finder.apply(parentCandidates.intersect(finder.allCandidates));
  }
}
Run Code Online (Sandbox Code Playgroud)

它使用intersect 方法的darq,但如果您不想包含另一个包,您可以编写自己的库。

对于你的情况,它会像这样使用:

expect(
  find.chained([
    find.text('0'),
    find.byKey(const ValueKey('counter')),
  ]),
  findsOneWidget,
);
Run Code Online (Sandbox Code Playgroud)