Better Bloc and Cubit Unit Testing
Tags: flutter, testing, bloc, cubit
Published: 2021-07-16 18:12:05

---
title: Better Bloc and Cubit Unit Testing
published: true
description: Easy and dependency free setup for writing unit tests with flutter_bloc
tags: flutter, testing, bloc, cubit
//cover_image: https://direct_url_to_image.jpg
---

Similar to a [previous article](https://dev.to/danielcardonarojas/testing-mobx-stores-in-flutter-3m5e), in this one I would like to address some pain points I've encountered while working with the bloc package, specifically related to testing.

As you might have seen in the official bloc [documentation](https://bloclibrary.dev/#/testing), it promotes bloc_test package to facilitate writing unit tests.

After playing around with this package and the main testing facility it exposes, I couldn't write the kind of unit tests I was looking for.

## So what's the deal 🤷🏽‍♂️

The problem is trying to assert that a certain state has being emitted independently of how current state has been built up or what its concrete value is.

`blocTest` does seem to have an option to skip a number of previously emitted values and to seed the current state and start from there. But this is a bit annoying in my opinion since you might not now how many emissions have been sent for a particular state to be reached
and using seed might be error prone and difficult to build the correct seed.

I love using the `matchers` package to do more flexible assertions, but to my surprise matchers didn't play around very nicely with `blocTest` function (I might be missing something, leave a comment if you know a workaround)

With `blocTest` you have to explicitly say what previous states have been (or skip them). 


```dart
blocTest(
    'emits [-1] when CounterEvent.decrement is added',
    build: () => counterBloc,
    act: (bloc) => bloc.add(CounterEvent.decrement),
    expect: () => [-1], // the history of all emitted states up until now :(
);
```

I tried using matchers in the `expect` parameter but did not work.

## Solution 🏆

Much like the solution proposed in the previous article about [mobx testing](https://dev.to/danielcardonarojas/testing-mobx-stores-in-flutter-3m5e) but this time wrapped in custom testing method.

This a what you get:

```dart
cubitTest('Emits loaded state when registration service succeeds',
    build: () => sut,
    stateField: (RegistrationState state) => state.status,
    arrange: () {
      when(
        () => mockRegistrationService.register(
            email: any(named: 'email'), password: any(named: 'password')),
      ).thenAnswer((_) async {
        return;
      });

      sut.updateEmail('daniel@gmail.com');
      sut.updatePassword('12345678');
    },
    act: (RegistrationCubit cubit) => cubit.register(),
    assertions: (MockCallable<Status> updatesStatusWith) {
      verify(() => updatesStatusWith(Status.loaded));
    });
```

This example can work perfectly with either: 

```dart
verify(() => updatesStatusWith(Status.loaded)),

// OR

verifyInOrder([
  () => updatesStatusWith(Status.loading),
  () => updatesStatusWith(Status.loaded),
]);
```

This is exactly what I want, I can do an assertion on particular property and don't need to  reason about what the previous states have to be and how to get there.



> Note in this example I'm using `mocktail` instead of `mockito` but either could be used. Same goes for cubit or bloc


**Brief explanation**


Very similar to `blocTest` this also gives the same feel for arrange, act, assert structure but with some key differences, here is a short explanation of critical parameters:

- `stateField` is interesting because it allows selecting a the particular state property you're interested in.

- `assertions` is a closure with a single parameter of type MockCallable<T> where T will be whatever you selected with `stateField`

> Note that stateField could be the identity function in which case you could do assertions over the entire state


Also you might think `build` should return new instances of the bloc or cubit but I actually reuse the same instance for all tests so I can use the setup method and benefit from that as well. 


And here is the implementation of the test wrapper: 
[github gist](https://gist.github.com/DanielCardonaRojas/2d6bf1f60d8efd821e9e3b92c7bfb5b1).

```dart
abstract class Callable<T> {
  void call([T? arg]) {}
}

class MockCallable<T> extends Mock implements Callable<T> {}

@isTest
void cubitTest<P, S, B extends BlocBase<S>>(
  String description, {
  required B Function() build,
  required FutureOr Function(B) act,
  required P Function(S) stateField,
  Function? arrange,
  Function(P)? inspect,
  required void Function(MockCallable<P> updatesWith) assertions,
}) async {
  test(description, () async {
    final expectation = MockCallable<P>();
    arrange?.call();
    final bloc = build();
    bloc.stream.listen((S state) {
      final focusedProperty = stateField(state);
      inspect?.call(focusedProperty);
      expectation(focusedProperty);
    });

    await act(bloc);
    await bloc.close();

    assertions(expectation);
  });
}
```

Hope you liked this and found it helpful in some way!

Until next time...