beer geek
beer geek

Reputation: 371

Unit testing Dart async stream

I am trying to create a list in Dart that sends events each time an object is added or removed from it, and I'm having trouble figuring out how to write a unit test for it.

Here's the code for the list:

enum Action { Insert, Modify, Remove }

class ListChangeEvent {
  final int index;
  final Action action;
  final PathComponent component;

  ListChangeEvent(this.index, this.action, this.component);
}

class PathComponentList extends DelegatingList<PathComponent> {
  final List<PathComponent> _list = [];

  @override
  List<PathComponent> get delegate => _list;

  var changeController = new StreamController<ListChangeEvent>();
  Stream<ListChangeEvent> get onChange => changeController.stream;

  @override
  void add(PathComponent value) {
    super.add(value);
    changeController
        .add(new ListChangeEvent(this.indexOf(value), Action.Insert, value));
  }
}

Now what I'd like to do is create a unit test to verify that each time I insert an element, I get a ListChangeEvent delivered in the stream.

Here's my current attempt. This hangs forever, I'm sure because I never close the stream.

void main() {
  group('PathComponentList', () {
    PathComponentList list = PathComponentList();
    var body = PathComponent(Path.Body);
    var chance = PathComponent(Path.Chance);
    var crossroads = PathComponent(Path.Crossroads);

    setUp(() async {
      list = PathComponentList();
    });

    test('should fire event when adding', () async {
      list.add(body);
      list.add(chance);
      list.add(crossroads);

      List<ListChangeEvent> events = await list.onChange.toList();
      expect(events[0].action, equals(Action.Insert));
      expect(events[0].index, equals(0));
      expect(events[0].component, equals(body));
    });
  });
}

I've tried to get individual element from the stream like this:

await list.onChange.elementAt(index);

But if I do this more than once, the code complains that I've already listened to the stream.

Since I want the stream to send ListChangeEvents for the lifetime of the application, what is the best way to read the three ListChangeEvents that are written to this stream in my unit test?

Upvotes: 1

Views: 1956

Answers (1)

Kevin Moore
Kevin Moore

Reputation: 6161

The problem: the events have already "left" by the time you register your onChange listener.

Try this: (modified since I don't have your PathComponent type)

test('should fire event when adding', () async {
  // Queue up the adds to happen after we listen for the change event
  Timer.run(() {
    list.add(body);
    list.add(chance);
    list.add(crossroads);
  });

  var events = await list.onChange.take(3).toList();

  expect(events[0].action, equals(Action.Insert));
  expect(events[0].index, equals(0));
  expect(events[0].component, equals(body));
});

Upvotes: 4

Related Questions