How can I start with Flutter Testing? — Part one —

Ana Polo
Flutter Community
Published in
9 min readNov 11, 2021

Hi everyone! 👋

These days I started my journey through flutter testing. This world was unknown to me but I knew that having testing in our applications it is super important and I decided to learn on my own.

Probably the code that I'm going to show you now could be better, but remember, it is my first time doing flutter testing and I want to show you what I did and of course, if you have any suggestions the code will be open source.

My idea with this repository is to have a place for everyone interested in learning testing, create new content, PRs, and collaborate all together.

So let's get started! 🙌

First of all, I want to explain what kind of project I created. I created an app to shows cats that came from an API Cat. In this case, I have created a bottom navigation widget to have three pages. In this article, we will focus on the first page.

Random cats🐱

This page is a random cat visualizer, you can have the power to see cats from every part of the world with a simple click, awesome, right? Jokes aside, the idea of this page is to allow the user to visualize random cats when clicking the floating action button.

To get that result I have kept the app structured simple, this article is not about architecture so I decided not spend a lot of time doing that and tried to focused on the aim of this project: testing. 🧪

Anyway, this is the structure of the app folders.

Success case

In this flow, the user does click on the floating button and this trigger a bloc event. Bloc is in charge of communicating with the repository to get the next cat. The repository calls to an API which gets the next random cat. In this case, the flow is successful so the service will return a Cat object to the repository and it will return to the bloc. Bloc will check if the cat is correct (if it has a non-empty breed list) and in this case emit RandomCatStatus.success. The view has a BlocConsumer with a builder that is listening for possible changes of state to rebuild the view.

Success case with an empty or null breed list

In this case, the service returns a correct answer, but the cat has an empty or null breed list. This list is necessary to display information about the cat. Therefore, when the repository returns the object to the bloc, it will be in charge of verify if this list is empty or null, in this example the list is empty so bloc it will emit a RandomCatStatus.emptyBreed. The view will be listening for this state on the listener in the BlocConsumer and when it happens the view should call the event again to get another random cat.

Throw an error when the response.body is empty

This is another case that could happen, when the service response with a 200 status code but the response body is empty, in this case, the service will throw an error. When an error is thrown the try/catch on the bloc will capture this error and will emit a RandomCatStatus.failure. The view will be listening to this state to show a message with the error.

Throw an error when the response.statusCode is not 200.

For example, if the response status code returns a 404 or a 400 the app will throw an error. Bloc will capture the error and will emit a RandomCatStatus.failure to the view.

Those are all the possible scenarios for the app.

Let's get into testing 🧪

It’s time to go through to testing in flutter. The first thing that we need is to have the correct dependencies to do testing into our pubspec.yaml.

Good, now we can start! ✅

Into a root project → test folder I have created this:

In flutter we can do different types of testing:

  • Unit testing
  • Bloc testing
  • Widget testing
  • Integration testing

Here we are going to be focusing on the first three.

When you are testing you have different methods to use after or before to doing a test.

Unit test

In this part I’m going to test the methods that I have created on the service and repository classes.

This is the service.dart class

In this case, I have to test all the possible scenarios that the service could have. To do that the first thing that I need to do is create mock classes for the response, objects, and so on.

Then into the main method, I have created a group to have all the tests related to the service class. In the setUpAll method, I have registered a registerFallbackValue(FakeUri()), due to I need it to use a fake URI into the test. If I don't do that I couldn't use the any() method.

Also, I have created the needed variables for the mocks. Those are will be instantiated into setUp method. In addition, I have created a JSON variable into a separate class to fake the result of the API (when API returns a correct cat).

The first test that I'm going to do is to check if the constructor does not require a httpClient.

Now I'm going to create a group to have all the test to get a random cat.

group(('catSearch'), () {});

Inside this group, I need to check different things.

  1. When the service does a correct HTTP request but the body is empty.

2. When the service throws an Error when the response it’s not a 200.

3. When the service returns a Cat.json on a valid response.

If you run all those tests you are going to get the best phrase: all tests passed!

Here is a complete class.

Now it’s time to do testing on the repository class. Here is the cat_respository class.

First, I created the necessary mocks.

Second, I have created a group to have all the tests related to this class. Here is the place to create the needed variables and initialize them into setUp method.

Next, I have created a test for the constructor, here I checked that the cat service is required.

Other checks to complete all the coverage for this class are:

  1. Call to search method.

2. Throw error when search method fails.

And this is the code of the cat_repository_test.dart class.

All tests passed!

Cool! 😎 Now we have all the tests passed into our service and repository, it’s time to get into bloc test.

Bloc Test

random_cat_bloc_test.dart

First of all, this is the random_cat_bloc class:

In this type of testing the syntaxis is a little bit different to the unit testing but it’s the same philosophy. Here is the official documentation about how tests bloc in flutter : Bloc State Management Library

I followed this documentation and I got to run my own bloc tests.

Let’s get started! 🙌

As always the first step that we need to do is to create the needed mock classes.

Next, we need to create and initialize the needed variables to our mock classes.

Also I created a first test to check if the initial state of the bloc is RandomCatStatus.initial.

And now we can start with our bloc testing.

  1. Emit RandomCatStatus.loading and RandomCatStatus.emptyBreeds when the list of breeds is empty.

2. Emit RandomCatStatus.loading and RandomCatStatus.emptyBreeds when the list of breeds is null.

3. Emit RandomCatStatus.loading and RandomCatStatus.success when the response is correct and cat has breeds.

4. Emit RandomCatStatus.loading and RandomCatStatus.failure when unsuccessfull.

This is all the random_cat_bloc_test class!

Additionally, you can test the states and events of your application.

Here is the random_cat_state_test.dart.

And here is the random_cat_event_test.dart.

All tests passed!

Now we are done with the part of unit testing and bloc testing, it’s time to start with Widget testing!

Widget testing

This kind of testing is super cool, you can test the possible interactions of the user with the application, so you can have full control over all the states of your application.

In this case the widget testing is going to be easy.✌️

Let’s get started!

First we need to test our random_cat_page.

Again, we need to create the fake classes.

Then we need to register into setUpAll method.

In this case we only need to test that the page is rendered fine.

Here is the complete class.

All tests passed!

The next class that we need to test is : random_cat_layout_test.dart.

Here I have created different types of views depending on the state of the view. To testing those views the most important part is the Key.

Views by states.

This class has more tests to do. Here we have different states and a button interaction, so let’s get into it.

First we need the mock and fake classes.

Then we need to create and initialize the variables.

The first thing that we are going to test is the possible states of the view, to do that we are going to create a specific group and then inside it, we can test all the cases.

group('RandomCatPage states ', () {});
  1. Render the view FailureRandomCatView when state is RandomCatStatus.failure.

2. Render the view LoadingView when state is RandomCatStatus.loading.

3. Render the view SuccessRandomCatView when state is RandomCatStatus.success.

4. Render the view InitialRandomCatView when state is RandomCatStatus.initial.

Cool 😎! Now we have tested all the possible states for the view, we could start with the next step: Test the button click!

  1. Call to the event SearchRandomCat when the button is clicked.

And finally, we need to control the part when the cat has no breeds. We are listening inside the BlocConsumer so if it happens we need to call the event SearchRandomCat again, for the test that we need to do the following:

Here is the complete class.

All tests passed!

The last step that we have to do is check the coverage. You have different options:

  1. Run flutter test — coverage on the terminal.
  2. Install LCOV to visualize in HTML

I used the second option, so I’m going to show you how can install and use it. (Installation for mac).

  1. brew install lcov
  2. flutter test — coverage
  3. genhtml coverage/lcov.info -o coverage/html

And finally, you are going to see the 100% coverage of our application 🧪✌️

Conclusion

I think that learning testing at the beginning could be hard and frustrating, but you should learn it because if you want to build good applications you need to understand how the test works. In my first approach with it, I resolved a lot of bugs that would be there if I hadn’t done the testing.

So …please do testing!

Repository

As I told you at the beginning of the article, I would like that this project will be open-source, so feel free to add new content, PRs, issues or whatever you want. I hope that this repository will be helpful to learn flutter test in an interactive way.

Github: https://github.com/AnnaPS/cats_flutter_bloc

Thanks for read it!

Fun fact: I took the base of the project of chapter 3 of Flutter apprentice book! If you don’t know what it is please go into : https://www.raywenderlich.com/books/flutter-apprentice/v1.0.ea2

Thanks to Very Good Ventures Team for the basics in testing with the course https://www.youtube.com/watch?v=M_eZg-X789w&list=PLprI2satkVdFwpxo_bjFkCxXz5RluG8FY.

Also, thanks to Felix Angelov for the examples of the bloc library repository, https://github.com/felangel/bloc/tree/master/examples .

Special thanks to Oscar Martin to check the article and helping me with the english and testing part 😊.

You can find me on twitter: AnaPolo_dev .😊

Follow Flutter Community on Twitter: https://twitter.com/FlutterComm

--

--

Ana Polo
Flutter Community

Software Engineer at Very Good Ventures. 🦄 Co-Organizer of @FlutterMadrid & @flutter_es communities. The organizer of @es_flutteristas. Github: AnnaPS 💙