Home > Blockchain >  Assert all the items emitted by a Stream in order until it is canceled in Dart?
Assert all the items emitted by a Stream in order until it is canceled in Dart?

Time:07-06

Is there any convenient way to assert all the items emitted by a Stream in order until it is canceled?

If I use:

expectLater(
  stream,
  emitsInOrder(<String>[
    'item1',
    'item2',
  ]),
);

and the Stream emits ['item1', 'item2', 'item3'] , the test won't fail.

The only way I've found so far is the following:

var count = 0;
final expected = ['item1', 'item2', 'item3'];
stream.listen(
  expectAsync1(
    (final result) {
      expect(result, expected[count  ]);
    },
    count: expected.length,
  ),
);

But it is a bit verbose and not very easy to read. Is there a simpler/more elegant way?

CodePudding user response:

You can collect the items into a list, using toList, then compare it to your own expectation list:

await expectLater(stream.toList(), completion(expected));

This does not handle the case where the stream doesn't close at all (but then, nothing does, you just have to wait for a timeout).

It doesn't catch errors until all events have been emitted, the emitsInOrder approach is better for that. Not shorter, though.

CodePudding user response:

emitsDone can be used if the Stream is closed at some point.

E.g:

test('Test', () async {
  final controller = StreamController<String>();
  final stream = controller.stream;
  final matcher = expectLater(
    stream,
    emitsInOrder(<dynamic>[
      'Item1',
      'Item2',
      emitsDone,
    ]),
  );
  controller
    ..add('Item1')
    ..add('Item2')
    ..add('Item3')
    ..close();
  await matcher;
  await controller.close();
});

The test fails with error:

Expected: should do the following in order:
          • emit an event that 'Item1'
          • emit an event that 'Item2'
          • be done
  Actual: <Instance of '_ControllerStream<String>'>
   Which: emitted • Item1
                  • Item2
                  • Item3
                  x Stream closed.
            which didn't be done

As @Irn suggest, a more compact alternative for Streams that complete at some point is using toList:

test('Test', () async {
  final controller = StreamController<String>();
  final stream = controller.stream;
  final matcher = expectLater(stream.toList(), completion(<String>['Item1', 'Item2']));
  controller
    ..add('Item1')
    ..add('Item2')
    ..add('Item3')
    ..close();
  await matcher;
  await controller.close();
});

If the Stream is never closed, you can add a timeout and check the items that have been emitted in that period:

test('Test3', () async {
  final controller = StreamController<String>();
  final stream = controller.stream.timeout(const Duration(milliseconds: 200));
  final matcher = expectLater(
    stream,
    emitsInOrder(<dynamic>[
      'Item1',
      'Item2',
    ]),
  );
  controller
    ..add('Item1')
    ..add('Item2');
  await matcher;
  await controller.close();
});
  • Related