Search code examples
flutterunit-testingbloc

Flutter Mocktail with bloc_test: how to verify another bloc's state is changed


I try to verify the state of another bloc is changed with bloc_test but not sure how to do.

The situation is like that

  • BlocA has an event called SignIn
  • BlocB has an item in state called User, which is updated with event called UserChanged

I would like to verify that when BlocA.add(SignIn()), the User in BlocB is updated with specific values.

My current idea is

act: (blocA) {
        blocA.add(SignIn());
      },
verify: (bloc) {
        verify(() => BlocB.add(
              UserChanged(
                user: any(
                  named: "user",
                  that: isA<User>().having(
                    (user) => user.email,
                    "email",
                    "[email protected]",
                  ),
                ),
              ),
            )).called(1);
      },

But I got the following error

Invalid argument(s): An ArgumentMatcher was declared as named user, but was not passed as an argument named user.

BAD:  when(() => obj.fn(any(named: "a")))
GOOD: when(() => obj.fn(a: any(named: "a")))

Please advice how I can achieve it. Thank you

===============================================

Update: my test will pass if I do not verify the argument passed to UserChanged event, such as

act: (blocA) {
        blocA.add(SignIn());
      },
verify: (bloc) {
        verify(() => BlocB.add(any<UserChanged>())).called(1);
      },

Solution

  • yes, at least I did the same. You need to check the proper states and method execution separately.

    test(
      'WHEN user does smth'
      'THEN adds new states for Bloc A and Bloc B',
      () async {
        await _callUseCase();
    
        verify(blocA.setLoaded()).called(1);
        verify(blocB.setChanged()).called(1);
      },
    );
    

    So the test above is for that use case:

    class UseCase {
     const UseCase({
    required this.blocA,
    required this.blocB,
    required this.repoA,
    });
    
    final BlocA blocA;
    final BlocB blocB;
    final RepoA repoA;
    
    Future<bool> call(Payload payload) async {
      final List<Account> accounts = blocA.state.allTradingAccounts;
      try {
        blocA.setUpdating();
        final result = await repoA(payload);
        blocA.setLoaded([
          result,
          ...accounts,
        ]);
        blocB.setChanged(MyType.real);
        return true;
      } catch (error, stack) {
        blocA.setLoaded(accounts);
        return false;
      }
    }