Skip to content

Instantly share code, notes, and snippets.

@dumazy
Last active March 6, 2024 18:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dumazy/ff362af06e1b2824f2931f721bc6434f to your computer and use it in GitHub Desktop.
Save dumazy/ff362af06e1b2824f2931f721bc6434f to your computer and use it in GitHub Desktop.
A utility to wait for notifyListeners to be called
// When there are external events triggering notifyListeners() in a ChangeNotifier,
// how can we easily test them? Not sure if something like this `ChangeNotifierListener` below
// is a valid approach, or if there are easy ways to test `ChangeNotifier` in a unit test, without a widget test.
// Just for sake of the example
class MyViewModel with ChangeNotifier {
MyViewModel(Stream<Object> eventStream) {
eventStream.listen((event) {
_counter++;
notifyListeners();
});
}
int _counter = 0;
int get count => _counter;
}
// A utility class for unit testing ChangeNotifier
class ChangeNotifierListener {
ChangeNotifierListener(this._changeNotifier) {
_changeNotifier.addListener(_incrementTimesNotified);
}
final ChangeNotifier _changeNotifier;
int _timesNotified = 0;
int get timesNotified => _timesNotified;
Completer<void>? _completer;
void _incrementTimesNotified() {
_timesNotified++;
if (_completer != null) {
_completer!.complete();
}
}
Future<void> untilNotified() async {
if (_completer != null) {
throw StateError('Cannot call untilNotified while waiting for a previous call to complete');
}
_completer = Completer<void>();
await _completer?.future;
}
void dispose() {
_changeNotifier.removeListener(_incrementTimesNotified);
}
}
extension ChangeNotifierListenerExtension on ChangeNotifier {
ChangeNotifierListener listen() => ChangeNotifierListener(this);
}
// Usage
test('MyViewModel should notify listeners when an event comes in', () async {
final streamController = StreamController<Object>();
final viewModel = MyViewModel(streamController.stream);
final listener = viewModel.listen();
expect(viewModel.count, 0);
streamController.add(Object());
// Without waiting here, the viewModel.count would still be at 0
await listener.untilNotified();
expect(viewModel.count, 1);
expect(listener.timesNotified, 1);
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment