flutterdart

Unit testing dart Timer issue, or bad code


I have the following class:

class MyDebounceBuffer<T> {
  MyDebounceBuffer({required this.duration});

  final Duration duration;

  final _controller = StreamController<List<T>>.broadcast();
  Stream<List<T>> get stream => _controller.stream;

  final _buffer = List<T>.empty(growable: true);

  Timer? _timer;

  void add(T item) {
    _timer?.cancel();
    _buffer.add(item);
    _timer = Timer(duration, () => emit());
  }

  void emit() {
    _timer?.cancel();
    _controller.add(_buffer);
    _buffer.clear();
  }
}

It is supposed to behave similarly to the debounceBuffer in Pub.dev stream_transform except that it has the option to emit immediately.

When we unit test the code - if fails!

test('MyDebounceBuffer example', () async {
    final controller = MyDebounceBuffer<int>(duration: const Duration(milliseconds: 10));

    var emitted = false;
    controller.stream.listen(expectAsync1(
      (p0) {
        expect(p0, [0, 1, 2, 3]);
        emitted = true;
      },
    ));
    controller.add(0);
    await Future.delayed(const Duration(milliseconds: 5));
    controller.add(1);
    await Future.delayed(const Duration(milliseconds: 5));
    controller.add(2);
    await Future.delayed(const Duration(milliseconds: 5));
    controller.add(3);
    // Extend the duration of the test to capture the debounceBuffer emission
    await Future.delayed(const Duration(milliseconds: 20));
    expect(emitted, true);
  });

We end up with an empty list being emitted. I can re-write the code to use a new List per event, but I would like to understand what is happening here, or is there a better way to do this?


Solution

  • Do one change in your emit() function to achieve this.

    void emit() {
    _timer?.cancel();
    
    _controller.add([..._buffer]); // Use this
    //_controller.add(List<T>.from(_buffer)); // or this
    
    _buffer.clear();}
    

    Because _controller.add(_buffer); only send memory address and in next step you used _buffer.clear() which clear data from list. But List<T>.from(_buffer) and [..._buffer] creates a new list in memory.