Boost Your Unit Testing in Flutter: Utilities to Save Time and Write Tests Faster and Simpler!

Carlos Daniel
3 min readJul 25, 2023
Photo by Yuriy Vertikov on Unsplash

Without delving into the myriad concepts of unit testing in software development, which are extensively covered in numerous bibliographic references, this time I will share a set of utilities that we frequently use in our development team. These utilities have helped us save a significant amount of time while writing our unit tests.

Mockito Extensions

In our unit tests, we rely on Mockito (based on the Java Mockito framework) as a library for mocking objects that are part of our business logic. To streamline and enhance the writing of our tests within our Dart context, we make use of Dart extensions. These extensions allow us to easily and flexibly mock various scenarios such as testing and mocking a success flow (Success), a Future, a response to a Stream in order, or a failure (Failure).

For instance, to mock a Success response or a Stream, we use the following approach, which saves us from having to write post-execution answers for each “when” statement:

when(pushNotifier.requestPermissions()).thenAnswer((realInvocation) => <<something to match>>);
when(pushNotifier.onTokenRefresh()).thenAnswer((realInvocation) => <<Some stream>>);

And we simply call the extension (thenSucceedthenStream).

Builders and Mother Objects

It’s a peculiar mix that we use, but I believe we get the best of both worlds for our benefit. We create general test data for our objects to facilitate testing scenarios through the Builder pattern and Mother Objects. Thus, we often separate our testing objects by context, which includes:

  • API (associated with the network calling layer — endpoints).
  • HTTP (HTTP response objects or failures with an error code or a null response).
  • Model (objects specific to our business model).
  • Libraries (objects from external libraries that we use within our app, over which we have no control).

Therefore, here are some examples (we won’t share all of them, just a few mixed ones that serve as references; everyone can arrange and refine them according to their project’s needs):

Stubs

We know that within the world of unit testing, there are several terms to refer to small pieces of functionality and/or responses/results to certain events, and Stubs are one of them. We won’t go into detail about each reference; we’ll just share this interesting article written by Martin Fowler that helps to clarify some of these concepts (this article is not the ultimate word, but it presents differences to consider in a simple way).

For our team, the use of Stubs has been very useful in our unit tests when, for example, we need to have small pre-loaded objects in memory to help define the environment of a test. For instance, we have an object called Storage that encapsulates a small set of data in memory through SharedPreferences. This Storage object is passed into the constructor of other objects, such as SessionStorage, through dependency inversion.

If we want to test business logic that involves SessionStorage, we create a Stub of Storage and set the necessary data on this object so that the tests can cover different scenarios:

And this is how we use it:

Now, today, we’ll stop here, but we believe that with these 3 utilities, we cover a lot of scenarios in our unit tests in a simple, readable, and easy-to-write manner.

Probably, in the future, we’ll have some additional details and other best practices to share in this amazing world of unit testing, which has been a great benefit for our development team and will likely be beneficial for your team as well.

Acknowledgements to Daniel Gómez and Julián Sotelo, members of my team, who are also the material and intellectual authors of these practices.

--

--

Carlos Daniel

Android & Flutter Developer. GDE for Android & Mobile Engineer.