【问题标题】:How to test a stream in Dart如何在 Dart 中测试流
【发布时间】:2018-08-21 21:30:42
【问题描述】:

如何在 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();
  }
});

我需要测试成员数据Word::contentString 是否等于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;
}));

【问题讨论】:

    标签: dart reactive-programming


    【解决方案1】:

    如果您的流正在发射具有您想要测试的属性的对象,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)
    );
    
    

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

        (record) {
          expect(record.name, expectedRecords[i].name);
          i++;
        }
    

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

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

    对于上面的例子,expectAsync1 有(可选的)类型参数:&lt;void,Record&gt; 第二个类型参数Record 告诉封闭的函数发出的流项目是Record 类型,允许我使用类似的属性record.name 没有强制转换。

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

    最大参数

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

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

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

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

    RxDart

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

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

    【讨论】:

      【解决方案2】:

      尝试使用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();
        }
      });
      

      【讨论】:

      • 嗨@Kevin,感谢您的回复,但您提供的内容并未回答我的问题。我想在 emitsInOrder 中检查从 Stream 发出的内容是否是 Word(对象),以及类型为“String”的字段“content”是否与 List.generate 中提供的“String”相同
      • 类似这样的东西(此代码是假设的):expect(stream, emitsInOrder(List.generate( 6, (i) => i > 2 ? Word('') : Word( i.toString()))), (Word 实际, Word 预期) { return actual.content == expected.content; });
      【解决方案3】:

      在阅读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;
        }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-06-09
        • 1970-01-01
        • 1970-01-01
        • 2014-03-06
        • 2022-01-14
        • 2015-11-27
        • 1970-01-01
        相关资源
        最近更新 更多