DEV Community

Cover image for A Comprehensive Guide to Mockito in Flutter
Aswin Gopinathan
Aswin Gopinathan

Posted on

A Comprehensive Guide to Mockito in Flutter

No one likes testing! Everyone thinks testing is a boring job. But trust me, testing a software is as important as developing a software.

Imagine, if you spend days and months building your app and finally when it went live, the app started crashing. What if you had tested all the functionalities before making it live?

So, testing is a very important phase of Software Development Life Cycle. Even if you hate it, you have to do it.

But, in this article i will make sure you will end up liking the process of testing.

Celebration

So, what are you gonna learn in this article?

Just one thing - How to Unit test your code using Mockito!

Before i begin, if you wanna read about Unit Testing, check out my latest tweet which talks about a basic introduction to Unit Testing in Flutter:

Tweet

https://twitter.com/gopinathanaswin/status/1470617070559711234?s=21

Now, lets get to the fun part.

What is Mockito?

Mockito is a package in Flutter that helps you create Mock Dependencies of classes while testing your code.

Let me give you an example:
Imagine a feature that you are testing requires data to be fetched from an external DB such as Firebase/Supabase.
When you are testing your code offline you cant possibly gather data from the DB right? Or even if you are online, you dont wanna wait for the actual data to return in order to test the app right?

So, what will you do then?

Joey Thinking

We use Mockito to create a fake class which will use dummy data as input for your tests !!

Before we get started with the code, lets add dependencies for two packages in pubspec.yaml :

dev_dependencies:
  mockito: ^5.0.10
  build_runner: ^2.1.2
Enter fullscreen mode Exit fullscreen mode

Note: I have added the packages in the dev_dependencies section. This is because, we are not gonna use these packages in the actual application code.

Also, the package versions may vary when you are reading this article. Do checkout pub.dev to get the latest versions.

Let's create a new file cat.dart in the test folder:

class Cat {
  String sound() => "Meow";
  bool eatFood(String food, {bool? hungry}) => true;
  Future<void> chew() async => print("Chewing...");
  int walk(List<String> places) => 7;
  void sleep() {}
  void hunt(String place, String prey) {}
  int lives = 9;
}
Enter fullscreen mode Exit fullscreen mode

I have used the example which is given in the official docs of mockito in pub.dev

Next, create a file cat_test.dart file which will contain all the codes to test the class.
Add the following contents to the file:

import 'package:mockito/annotations.dart';
import 'cat.dart';

@GenerateMocks([Cat])
void main() {

}
Enter fullscreen mode Exit fullscreen mode

@GenerateMocks([Cat]) is used to create a Mock Class for our Cat class.

Now, head over to the terminal and type the following command:

dart run build_runner build
Enter fullscreen mode Exit fullscreen mode

This will generate the mock file for us with the name cat_test_mocks.dart which contains the MockCat class which we are gonna use for our testing.

Import this new file into our cat_test.dart file:

import 'cat_test.mocks.dart';
Enter fullscreen mode Exit fullscreen mode

Now, head over to the main function and initialise an object for class MockCat.

var cat = MockCat();
Enter fullscreen mode Exit fullscreen mode

Now, let's verify some methods. But, what is verify in testing ?

Verify: It means we are verifying a method if it was invoked in that particular scope or not.

I will explain this better with an example. Append the following code inside the main() function:

test('verify sound',() {
  when(cat.sound()).thenReturn('meow');

  cat.sound();

  verify(cat.sound());
});
Enter fullscreen mode Exit fullscreen mode

We have defined a test block that performs verify operation on the sound() method.

The first line:

when(cat.sound()).thenReturn('meow');
Enter fullscreen mode Exit fullscreen mode

is known as Stubbing.
Oh come on! What is Stubbing now? I will explain this a bit later, but for now just imagine we have a method sound(), that returns 'meow'.

Now, the second line:

cat.sound();
Enter fullscreen mode Exit fullscreen mode

we invoke the sound() method.

Finally, the last line:

verify(cat.sound());
Enter fullscreen mode Exit fullscreen mode

we are verifying if the mentioned method with the given parameter list was invoked in this scope or not.

So, this test will pass since it was invoked in line 2. But what if we write verify like this:

verify(cat.sound('Bow'));
Enter fullscreen mode Exit fullscreen mode

This will fail, because the sound() method was not invoked in this scope with a String parameter 'Bow'.

So, this is how verify works in mockito.

Where is this helpful ?
This is helpful to know whether a method was invoked during the flow of a unit.
For example: Verify if the validatePassword() method was called while creating a user.

But, there are some more interesting variants of verify known as verifyInOrder(), verifyNever(), and verifyZeroInteractions().

  1. verifyInOrder() takes in a list of methods, to verify if they were invoked in that particular order.

  2. verifyNever() takes in a method, to verify if it was never invoked.

  3. verifyZeroInteractions() takes in a mock object (not a method), to verify if that object was never called in the scope of the test.

Now, lets jump into Stubbing.

Stubbing is the process of overriding the behaviour of a method belonging to the mock class instance.

Example:

Consider a method that returns 'Dart':

String getLang() => 'Dart';
Enter fullscreen mode Exit fullscreen mode

We stub the method to return 'Flutter' instead.

when(obj.getLang()).thenReturn('Flutter');
Enter fullscreen mode Exit fullscreen mode

Now, no matter how many times you call the method getLang(), it will always return 'Flutter' as opposed to its declaration in its class.

This is helpful when you wanna test your methods for different return data.

Let's consider our MockCat class and test a stubbing:

test('stubbing',() {
  when(cat.sound()).thenReturn('Flutter');
  expect(cat.sound(), 'Flutter');
});
Enter fullscreen mode Exit fullscreen mode

In the normal flow cat.sound() should return 'Meow', but after stubbing it will always return 'Flutter'.

If a method invocation matches multiple stubs, the one which was declared last will be used. It is worth noting that stubbing and verifying only works on methods of a mocked class; in this case, an instance of MockCat must be used, not an instance of Cat.

Best Practices while working with Mockito

  • Whenever you make changes to the class that you are mocking, always re-generate the mock class using the build command that i mentioned above.

  • Always Stub your method before verifying it in a test block. Otherwise, it will return an error like the following:

Image description

  • Testing with real objects is preferred over testing with mocks - if you can construct a real instance for your tests, you should!

If you liked this article, dont forget to hit the like button and share it with your friends and colleagues.

Follow me on Twitter @GopinathanAswin where i regularly share my learnings in Dart and Flutter πŸ’™

Image description

Discussion (0)