lundi 18 janvier 2021

Flutter test await cancel of stream

Let's say I have code like this (bear with me, it's really a minimal example):

import 'dart:async';

import 'package:fake_async/fake_async.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/mockito.dart';

// application
class StreamService {
  Stream<int> makeStream() => Stream.periodic(const Duration(milliseconds: 500), (index) => index);

  void done() => print('done');
}

class TestedClass {
  StreamSubscription subscription;
  StreamService service;

  TestedClass(this.service) {
    subscription = service.makeStream().listen((data) => print(data));
  }

  Future<bool> testedMethod() async {
    await subscription.cancel(); // this line is the culprit
    await Future<void>.delayed(const Duration(seconds: 2));
    service.done();
    return true;
  }
}

// tests
class StreamServiceMock extends Mock implements StreamService {}

void main() {
  test('delayed done', () {
    fakeAsync((zone) {
      final controller = StreamController<int>();
      final serviceInstance = StreamServiceMock();
      when(serviceInstance.makeStream()).thenAnswer((_) => controller.stream);
      final testedInstance = TestedClass(serviceInstance);

      expect(testedInstance.testedMethod(), completion(true));
      zone.elapse(Duration(seconds: 2));

      verify(serviceInstance.done()).called(1);
    });
  });
}

As you can see we have a class that takes a service providing a stream. It's then subscribing to the stream and when a special method is called it cancels the subscription, waits some time (showing user stuff) and then calls something else (service.done()).

Now we wanna write a test that checks if service.done is actually called. However, the cancellation of the subscription, as it's awaited, returns the control flow to the test and I haven't found any way to return to the tested method. Thus, the test fails.

How can I write a test that correctly tests if service.done is called (simply awaiting testedMethod is not good enough, because I also want a test that checks that done is not called before the 2 second mark).

Aucun commentaire:

Enregistrer un commentaire