Olf
Olf

Reputation: 31

How to mock a StateNotifier in flutter / Riverpod

I wrote a simple provider in flutter that should provide the current date/time (clock.now() is from the clock package):

final clockProvider = StateNotifierProvider<Clock, DateTime>((ref) {
  return Clock();
});

class Clock extends StateNotifier<DateTime> {
  late final Timer _timer;
  Clock() : super(clock.now()) {
    _timer = Timer.periodic(const Duration(seconds: 1), (_) {
      state = clock.now();
    });
  }

  @override
  void dispose() {
    _timer.cancel();
    super.dispose();
  }
}

One reason for that is that I thought it would be easier this way to test with a given set time using Mockito:

@GenerateNiceMocks([
  MockSpec<Clock>(),
])

void main() {
    testWidgets(
        "test clock", (tester) async {

      // arrange
      final MockClock mockClock = MockClock();
      when(mockClock.state).thenReturn(DateTime(2000));
      final container = ProviderContainer(
        overrides: [
          clockProvider.overrideWithValue(mockClock),
        ],
      );
      await container.pump();
      final result = container.read(clockProvider);
    });

This throws the following exception:

══╡ EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK ╞════════════════════════════════════════════════════
The following ProviderException was thrown running a test:
An exception was thrown while building StateNotifierProvider<Clock, DateTime>#0cdfd.

Thrown exception:
Bad state: Tried to read the state of an uninitialized provider

When following this approach and overwriting not with a value but with a provider the error message is the same and flutter still complains about "Tried to read the state of an uninitialized provider":

      final MockClock mockClock = MockClock();
      when(mockClock.state).thenReturn(DateTime(2000));
      final mockClockProvider = StateNotifierProvider<Clock, DateTime>((ref) {
        return mockClock;
      });
      final container = ProviderContainer(
        overrides: [
          clockProvider.overrideWithProvider(mockClockProvider),
        ],
      );

Would appreciate any help in finding the mistake!

Upvotes: 0

Views: 706

Answers (1)

AxisStarstreamer
AxisStarstreamer

Reputation: 68

If you want to test with a given time, the approach you linked basically does this by providing arguments for your constructor. In your case it would be something like

class ClockNotifier extends StateNotifier<DateTime> {
  late final Timer _timer;
  ClockNotifier(Clock clock = clock.now()) : super(clock) {
    _timer = Timer.periodic(const Duration(seconds: 1), (_) {
      state = clock.now();
    });
  }

Note: Doing state = clock.now(); in the Timer.periodic() would override everything you set in the constructor. But it might not effect it for your test.

Second note: you should rename it to ClockNotifier as you are using the Clock package

I hope that helps, I am doing Flutter only since a few month now.

Upvotes: 0

Related Questions