rrousselGit / state_notifier

ValueNotifier, but outside Flutter and with some extra perks
MIT License
311 stars 28 forks source link

Mocking StateNotifier #56

Closed Norbert515 closed 3 years ago

Norbert515 commented 3 years ago

Hey,

I want to mock a StateNotifier so I can control what states are emitted.

For this I created:

class MockStateNotifier<T> extends StateNotifier<T> with Mock {
  MockStateNotifier(T state) : super(state);
}

to mock all custom methods declared but to retain the listening logic.

In a test I can do:

class MockRegistrationStateNotifier extends MockStateNotifier<RegistrationState>
    implements RegistrationStateNotifier {
  MockRegistrationStateNotifier() : super(RegistrationState.unauthenticated());
}

[...]

    mockRegistrationStateNotifier.state = RegistrationState.error(emailError: 'error');
    await tester.pump();
    expect(find.text('error'), findsOneWidget);

This works perfectly fine, the only issue I have with this is that the linter complains as accessing state is protected. Unfortunately I cannot use debugState here as it only exposes a getter.

Is there another way to do this? If not, could 'debugState' also expose a way of setting the state?

Thanks in advance!

rrousselGit commented 3 years ago

You can override set state to remove the @protected or you can create a debugSetState

Norbert515 commented 3 years ago

Yeah, totally forgot about that. That works like a charm - thanks!

PandaGeek1024 commented 2 years ago

@Norbert515 which solution did you used, I was trying to create a debugSetState but don't know how to set the _state since it is private.

PandaGeek1024 commented 2 years ago

@rrousselGit could you please add an example of how the set state or debugSetState can modify the private _state variable?

Norbert515 commented 2 years ago

The state variable is private indeed, but there is the setter 'state' which is only protected. So you can simply call state = ... in your subclass.

wwwdata commented 2 years ago

Hm, I am also thinking about how to do it best. So what works easily is:

But: when I want to write widget tests, I sometimes don't want to use the real StateNotifier but a Mocked version of it, that I can control. I don't want to use the real thing and mock all dependencies, because then you end up mocking a lot, and I already unit tested my StateNotifier anyways already. When using flutter_bloc before, they provided a MockBloc implementation that could be used for this scenario. You could then input a mocked initial state and even a series of changes as a Stream. And it was working flawlessly in widgets. Of course you could write this on your own and make sure that all the addListener and whatnot calls work correctly and your widgets render with the mocked StateNotifier but since this is such a common thing that you need for testing, I think there should also be a standard MockStateNotifier class that is ready to use, or?

A typical use case then would be: