Testing Mobx stores in Flutter

I’ve been using mobx in a couple of projects and have enjoyed its pragmatic and boilerplate free approach to state management.

Having said that when it came to testing I did not find a good way to write the kind of tests I wanted, although the documen…


This content originally appeared on DEV Community and was authored by Daniel Cardona Rojas

I've been using mobx in a couple of projects and have enjoyed its pragmatic and boilerplate free approach to state management.

Having said that when it came to testing I did not find a good way to write the kind of tests I wanted, although the documentation for mobx in in Flutter is great https://mobx.netlify.app/ I found it lacking when trying to figure out how write robust unit tests. Luckily I found quite a nice approach that I'd like to share in this post.

The main requirement I had was to be able to test multiple state changes within a single action in a store.

Let me give a common example that motivates this requirement. Lets suppose we have a store that fetches items of a feed.

Most of the time we want to have some state property that reflects if the feed is in the loading, has failed to fetch items or has finally received the items from a service

The problem

In mobx stores a give action can mutate an observable property multiple times within a given action

For example:

@observable
Status status = Iddle();

@observable
List<Item> items = [];

@action
Future<void> getItems() async {
  status = Loading();
  items = await _service.getItems();
  status = Loaded();
}

Since state management in mobx unlike bloc is not stream based. Its not obvious how to check intermediate mutations of an observable property within the action. If you await the action you will only be able to inspect the final value the inspected property.

test('store emits status values for loading, loaded when fetching items', () async {
  // arrangement code

  assert(store.status == Iddle());
  await store.getItems();

  // Check that status passed through Loading value

  assert(store.status == Loading()); // Will fail!!!
  assert(store.status == Loaded());

});

Reactions ? to the rescue ?

The solution comes down to using a mobx reaction to observe mutations of a property within the store.

I'll introduce an extra class that is required for this approach

import 'package:mockito/mockito.dart';

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

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

So going back to our example this is how the test could be written:

test('store emits status values for loading, loaded when fetching items', () async {
    final statusChanged = MockCallable<Status>();

    when(mockService.getItems())
        .thenAnswer((_) async => fakeItems);

    mobx.reaction<Status>(
        (_) => sut.status, (newValue) => statusChanged(newValue));

    await sut.getComments();

    verifyInOrder([
      statusChanged(Loading()),
      statusChanged(Loaded()),
    ]);

});

This actually turns out to be easier to test then BLOC in my opinion, since bloc tests require you to specifically state the exact value for all emitted states. In mobx using this approach you can opt in to this by testing a single mutation or a sequence of mutations ocurring in an action using any of the matchers that are provided out of the box by mockito.

Here are some other examples assertions that could be made in other test that showcase the flexibility you get with
different types of matchers.

// Or
verify(statusChanged(any)).called(2);

// Or
verify(
  statusChanged(argThat(allOf(Loaded(), Loading())))
);

// Or
verify(
  statusChanged(argThat(anyOf(Loaded(), Loading())))
);

I suggest looking into the matchers package to see all the cool matchers.

Personally like anyOf matchers which allows to check that a particular value has been emitted without having to check previous or future values.

I hope you liked this post, let me know you opinions, cheers!


This content originally appeared on DEV Community and was authored by Daniel Cardona Rojas


Print Share Comment Cite Upload Translate
APA
Daniel Cardona Rojas | Sciencx (2022-10-02T14:19:56+00:00) » Testing Mobx stores in Flutter. Retrieved from https://www.scien.cx/2021/04/30/testing-mobx-stores-in-flutter/.
MLA
" » Testing Mobx stores in Flutter." Daniel Cardona Rojas | Sciencx - Friday April 30, 2021, https://www.scien.cx/2021/04/30/testing-mobx-stores-in-flutter/
HARVARD
Daniel Cardona Rojas | Sciencx Friday April 30, 2021 » Testing Mobx stores in Flutter., viewed 2022-10-02T14:19:56+00:00,<https://www.scien.cx/2021/04/30/testing-mobx-stores-in-flutter/>
VANCOUVER
Daniel Cardona Rojas | Sciencx - » Testing Mobx stores in Flutter. [Internet]. [Accessed 2022-10-02T14:19:56+00:00]. Available from: https://www.scien.cx/2021/04/30/testing-mobx-stores-in-flutter/
CHICAGO
" » Testing Mobx stores in Flutter." Daniel Cardona Rojas | Sciencx - Accessed 2022-10-02T14:19:56+00:00. https://www.scien.cx/2021/04/30/testing-mobx-stores-in-flutter/
IEEE
" » Testing Mobx stores in Flutter." Daniel Cardona Rojas | Sciencx [Online]. Available: https://www.scien.cx/2021/04/30/testing-mobx-stores-in-flutter/. [Accessed: 2022-10-02T14:19:56+00:00]
rf:citation
» Testing Mobx stores in Flutter | Daniel Cardona Rojas | Sciencx | https://www.scien.cx/2021/04/30/testing-mobx-stores-in-flutter/ | 2022-10-02T14:19:56+00:00
https://github.com/addpipe/simple-recorderjs-demo