When writing a unit test for a function, you may wish to mock or stub the results of other functions called from the test function. This post demonstrates how to use Mockito to write a unit test for a Flutter application (or any other Dart-based application).
Importing Mockito Into Flutter Project
This tutorial makes use of two libraries. The first is test, which allows you to write and run tests in Dart in a consistent manner. The other is mockito, which is used to mock functions and is based on Java’s Mockito. Run get pakcages after adding the following to the dev dependencies section of pubspec.yml.
Copied!dev_dependencies: mockito: 4.0.0 test: ^1.5.3
Project Structure For Unit Testing In Flutter Using Mockito
For instance, there’s a file called lib/example.dart that has a list of functions we’ll be testing. The test files should be saved in the test folder. It is preferable to follow the convention in order to keep the structure neat. The code and test files should have the same file path relative to lib and test, with _test appended to the test filename.
Copied!lib example.dart test example_test.dart
Code To Test Using Mockito
There is a basic method formatPostTitle in example.dart. It has one Post-type parameter. The method will insert ‘[New]’to the beginning of the title if the post is new. It makes a call to postHelper to detect whether the post is new or not. isNewPost.
Copied!#lib/example.dart import 'dart:async'; import 'package:flutter_app/post.dart'; import 'package:flutter_app/posthelper.dart'; class Example { PostHelper postHelper; Example() { this.postHelper = new PostHelper(); } Example.withMocks({ this.postHelper }); String formatPostTitle(Post post) { bool isNew = postHelper.isNewPost(post); return isNew ? '[New] ${post.title}' : post.title; } }
Unit Test
We’ll use the test package to perform the tests. The test file’s fundamental structure is shown below.
Copied!main() { group('formatPostTitle', () { test('when post is new', () async { } test('when post is new', () async { } } }
Everything inside main will be executed when you run the test file. The group element specifies what is being tested, whereas the test block specifies each test case.
We don’t care if postHelper.isNewPost works or not because we’re simply testing formatPostTitle. There are two possibilities. It will return true if the post is new. Otherwise, false will be returned. As a result, we’ll need two test cases, with each mocking postHelper.isNewPost to return true or false, as appropriate. As a result, we’ll need to simulate the PostHelper class.
Add the following outside main to make a replica of PostHelper.
Copied!class MockPostHelper extends Mock implements PostHelper Array
Here’s an example of how to spoof the isNewPost function.
Copied!when(mockedPostHelper.isNewPost(any)) .thenReturn(true);
If we put the code above in a test block, it will always return true when postHelper.isNewPost is called.
The final step is to verify if the outcome matches our expectations. Expect may be used in a variety of ways, the most basic of which is to send the expected value as the first argument and the result as the second.
Copied!import 'package:flutter_app/post.dart'; import 'package:flutter_app/posthelper.dart'; import 'package:mockito/mockito.dart'; import 'package:test/test.dart'; import 'package:flutter_app/example.dart'; class MockPostHelper extends Mock implements PostHelper Array main() { group('formatPostTitle', () { test('when post is new', () async { final mockedPostHelper = MockPostHelper(); when(mockedPostHelper.isNewPost(any)) .thenReturn(true); Post post = new Post( title: 'Flutter/Dart Tutorial', ); Example example = new Example.withMocks(postHelper: mockedPostHelper); expect('[New] Flutter/Dart Tutorial', await example.formatPostTitle(post)); }); test('when post is not new', () async { final mockedPostHelper = MockPostHelper(); when(mockedPostHelper.isNewPost(any)) .thenReturn(false); Post post = new Post( title: 'Flutter/Dart Tutorial', ); Example example = new Example.withMocks(postHelper: mockedPostHelper); expect('Flutter/Dart Tutorial', await example.formatPostTitle(post)); }); }); }
Unit Testing Function That Returns Future
Consider using Mockito to mock a function that returns a Future. It’s a little unique. For instance, there’s a new method Future isPostActive that calls postHelper.fetchPost, which returns Future, which we’d want to mock in the unit test.
Copied!import 'dart:async'; import 'package:flutter_app/post.dart'; import 'package:flutter_app/posthelper.dart'; class Example { PostHelper postHelper; Example() { this.postHelper = new PostHelper(); } Example.withMocks({ this.postHelper }); String formatPostTitle(Post post) { bool isNew = postHelper.isNewPost(post); return isNew ? '[New] ${post.title}' : post.title; } Future<bool> isPostActive(int id) async { try { Post post = await postHelper.fetchPost(id); return post.active == true; } catch (err) { return false; } } }
If the function returns Future, we must use thenAnswer instead of thenReturn.
Copied!when(mockedPostHelper.fetchPost(1)) .thenAnswer((_) async => Future.value( new Post( id: 1, userId: 1, title: 'Post Title', content: 'Post content...', active: false, ) ) );
If the called function produces an error, we must handle it. ThenThrow can be used to simulate an error.
Copied!when(mockedPostHelper.fetchPost(1)) .thenThrow(new Error());
The complete unit test for the isPostActive function is shown below.
Copied!import 'package:flutter_app/post.dart'; import 'package:flutter_app/posthelper.dart'; import 'package:mockito/mockito.dart'; import 'package:test/test.dart'; import 'package:flutter_app/example.dart'; class MockPostHelper extends Mock implements PostHelper Array main() { group('formatPostTitle', () { // ... }); group('isPostActive', () { test('when post is active', () async { final mockedPostHelper = MockPostHelper(); when(mockedPostHelper.fetchPost(1)) .thenAnswer((_) async => Future.value( new Post( id: 1, userId: 1, title: 'Post Title', content: 'Post content...', active: true, ) )); Example example = new Example.withMocks(postHelper: mockedPostHelper); expect(true, await example.isPostActive(1)); }); test('when post is inactive', () async { final mockedPostHelper = MockPostHelper(); when(mockedPostHelper.fetchPost(1)) .thenAnswer((_) async => Future.value( new Post( id: 1, userId: 1, title: 'Post Title', content: 'Post content...', active: false, ) )); Example example = new Example.withMocks(postHelper: mockedPostHelper); expect(false, await example.isPostActive(1)); }); test('when error', () async { final mockedPostHelper = MockPostHelper(); when(mockedPostHelper.fetchPost(1)) .thenThrow(new Error()); Example example = new Example.withMocks(postHelper: mockedPostHelper); expect(false, await example.isPostActive(1)); }); }); }
Conclusion
In this article, we learned the fundamentals of writing unit tests with Mockito to mock dependencies on other functions in Flutter. As always, If you have found this article useful do not forget to share it and leave a comment if you have any questions. Happy Coding
Leave a Reply