如何在 Dart 中测试流

Ark*_*kan 9 reactive-programming dart

如何在 dart 中测试流?我有这个代码:

test('words are reading sequentially correct', () {
  WordTrackerInterface wordTracker = WordTracker.byContent('0 1 2');
  wordTracker.setWordsCountPerChunk(1);
  var stream = wordTracker.nextWordStringStream();

  expect(
      stream,
      emitsInOrder(List<Word>.generate(
          6, (i) => i > 2 ? Word('') : Word(i.toString()))));

  for (int i = 0; i < 6; i++) {
    wordTracker.nextWord();
  }
});
Run Code Online (Sandbox Code Playgroud)

我需要测试该成员数据Word::content这是一个String等于在所提供的emitsInOrder

类似于以下内容的流:

expect(
    stream,
    emitsInOrder(List<Word>.generate(
        6, (i) => i > 2 ? Word('') : Word(i.toString()))),
    expect((Word actual, Word expected) {
  return actual.content == expected.content;
}));
Run Code Online (Sandbox Code Playgroud)

Kev*_*ore 11

尝试使用async/awaitexpectLater

test('words are reading sequentially correct', () async {
  WordTrackerInterface wordTracker = WordTracker.byContent('0 1 2');
  wordTracker.setWordsCountPerChunk(1);
  var stream = wordTracker.nextWordStringStream();

  await expectLater(
      stream,
      emitsInOrder(List<Word>.generate(
          6, (i) => i > 2 ? Word('') : Word(i.toString()))));

  for (int i = 0; i < 6; i++) {
    wordTracker.nextWord();
  }
});
Run Code Online (Sandbox Code Playgroud)


Bak*_*ker 9

如果您的流正在发出具有您想要测试的属性的对象,expectAsync1可以提供帮助:

List<Record> expectedRecords = [record1, record2, record3];
int i = 0;
recordStream.listen(
    expectAsync1<void,Record>(
    (record) {
      expect(record.name, expectedRecords[i].name);
      i++;
    }, 
    max: -1)
);

Run Code Online (Sandbox Code Playgroud)

在上面的示例中,expectAsync1包含一个匿名函数:

    (record) {
      expect(record.name, expectedRecords[i].name);
      i++;
    }
Run Code Online (Sandbox Code Playgroud)

Record每次Stream 发出a 时都会运行recordStream

in1expectAsync1您的封闭函数将采用的参数数量。大多数情况下,这将是 1.(record)是上面的一个参数。

对于上面的示例,expectAsync1具有(可选)类型参数:<void,Record>第二个类型参数Record告诉封闭的函数发出的流项是类型Record,允许我使用类似record.name不进行强制转换的属性。

第一个类型参数是封闭函数的返回类型。我使用它是void因为封闭的函数没有返回任何内容,它只是运行一个expectMatcher 并迭代一个计数器,该计数器用于迭代Record我期望按expectedRecords该顺序看到的列表(即)。

最大参数

您会注意到max: -1下面包含的函数。这是一个可选但重要的参数,用于expectAsync1指定我们期望的流项目/事件的数量。

1如果max未给出,则默认为如果发出超过 1 个事件,则测试将失败。

错误将是Callback called more times than expected (1).

在上面的示例中,我使用的-1意味着可以发出/测试无限的事件。如果您想测试从流中获取的项目/事件的数量,您可以指定一个非零数字,否则测试将失败。我可以用max: 3上面的例子。

接收达特

如果您使用 RxDart,BehaviorSubject请记住最近的流事件是在 上发出的listen。因此,在您的测试中,当您开始监听/使用时expectAsync1,将立即调用包含最新事件的封闭函数。

ReplaySubject将在 上发出所有先前的流事件listen


Ark*_*kan 8

在阅读dart文件中的源代码并在互联网上阅读后,我找到了解决方案:我需要创建一个自定义Matcher。我在笔记本电脑上测试了以下代码,并通过引用我的应用程序中的其他文件(如“WordTracker”),代码按预期运行。

test('words are reading sequentially correct', () {
    WordTrackerInterface wordTracker = WordTracker.byContent('0 1 2');
    wordTracker.setWordsCountPerChunk(1);
    var stream = wordTracker.nextWordStringStream();

    expect(stream, 
      emitsInOrder(List<Word>.generate(6, (i) => i > 2 ? Word('') : Word(i.toString())).map<WordMatcher>(
        (Word value) => WordMatcher(value))));

    for (int i = 0; i < 6; i++) {
      wordTracker.nextWord();
    }
  });


class WordMatcher extends Matcher {
  Word expected;
  Word actual;
  WordMatcher(this.expected);

  @override
  Description describe(Description description) {
    return description.add("has expected word content = '${expected.content}'");
  }

  @override
  Description describeMismatch(
    dynamic item,
    Description mismatchDescription,
    Map<dynamic, dynamic> matchState,
    bool verbose
  ) {
    return mismatchDescription.add("has actual emitted word content = '${matchState['actual'].content}'");
  }

  @override
  bool matches(actual, Map matchState) {
    this.actual = actual;
    matchState['actual'] = actual is Word ? actual : Word('unknown');
    return (actual as Word).content == expected.content;
  }
}
Run Code Online (Sandbox Code Playgroud)