Last active
March 6, 2024 18:32
-
-
Save dumazy/ff362af06e1b2824f2931f721bc6434f to your computer and use it in GitHub Desktop.
A utility to wait for notifyListeners to be called
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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