In certain cases, unit tests may depend on classes that fetch data from liveweb services or databases. This is inconvenient for a few reasons:
- Calling live services or databases slows down test execution.
- A passing test may start failing if a web service or database returnsunexpected results. This is known as a “flaky test.”
- It is difficult to test all possible success & failure scenarios using alive web service or database.Therefore, rather than relying on a live web service or database,you can “mock” these dependencies. Mocks allow us to emulate a liveweb service or database and return specific results depending on the situation.
Generally speaking, you can mock dependencies by creating an alternativeimplementation of a class. You can write these alternative implementations byhand or make use of theMockito package as a shortcut.
This recipe demonstrates the basics of mocking using the Mockito package.For more information, please see theMockito package documentation.
Directions
- Add the
mockito
&test
dependencies - Create a function to test
- Create a test file with a mock
http.Client
- Write a test for each condition
- Run the tests
1. Add the mockito dependency
In order to use the mockito
package, you first need to add it to thepubspec.yaml
file along with the flutter_test
dependency in thedev_dependencies
section.
You’ll also be using the http
package in this example, and will define thatdependency in the dependencies
section.
dependencies:
http: <newest_version>
dev_dependencies:
test: <newest_version>
mockito: <newest_version>
2. Create a function to test
In this example, you’ll want to unit test the fetchPost
function from theFetch data from the internet recipe.In order to test this function, you need to make two changes:
- Provide an
http.Client
to the function. This allows you to provide thecorrecthttp.Client
depending on the situation.For Flutter and server-side projects, you can provide anhttp.IOClient
.For Browser apps, you can provide anhttp.BrowserClient
.For tests, you provide a mockhttp.Client
. - Use the provided
client
to fetch data from the internet,rather than the statichttp.get
method, which is difficult to mock.The function should now look like this:
Future<Post> fetchPost(http.Client client) async {
final response =
await client.get('https://jsonplaceholder.typicode.com/posts/1');
if (response.statusCode == 200) {
// If the call to the server was successful, parse the JSON
return Post.fromJson(json.decode(response.body));
} else {
// If that call was not successful, throw an error.
throw Exception('Failed to load post');
}
}
3. Create a test file with a mock http.Client
Next, create a test file along with a MockClient
class.Following the advice in the Introduction to unittesting recipe,create a file called fetch_post_test.dart
in the root test
folder.
The MockClient
class implements the http.Client
class. This allowsyou to pass the MockClient
to the fetchPost
function,and allow you to return different http responses in each test.
// Create a MockClient using the Mock class provided by the Mockito package.
// Create new instances of this class in each test.
class MockClient extends Mock implements http.Client {}
main() {
// Tests go here
}
4. Write a test for each condition
If you think about the fetchPost
function, it will do one of two things:
- Return a
Post
if the http call succeeds - Throw an
Exception
if the http call failsTherefore, you’ll want to test these two conditions.You can use theMockClient
class to return an “Ok” responsefor the success test, and an error response for the unsuccessful test.
To achieve this, use the when
function provided by Mockito.
// Create a MockClient using the Mock class provided by the Mockito package.
// Create new instances of this class in each test.
class MockClient extends Mock implements http.Client {}
main() {
group('fetchPost', () {
test('returns a Post if the http call completes successfully', () async {
final client = MockClient();
// Use Mockito to return a successful response when it calls the
// provided http.Client.
when(client.get('https://jsonplaceholder.typicode.com/posts/1'))
.thenAnswer((_) async => http.Response('{"title": "Test"}', 200));
expect(await fetchPost(client), isInstanceOf<Post>());
});
test('throws an exception if the http call completes with an error', () {
final client = MockClient();
// Use Mockito to return an unsuccessful response when it calls the
// provided http.Client.
when(client.get('https://jsonplaceholder.typicode.com/posts/1'))
.thenAnswer((_) async => http.Response('Not Found', 404));
expect(fetchPost(client), throwsException);
});
});
}
5. Run the tests
Now that you have a fetchPost
function with tests in place,run the tests.
$ dart test/fetch_post_test.dart
You can also run tests inside your favorite editor by following theinstructions in the Introduction to unittestingrecipe.
Summary
In this example, you’ve learned how to use Mockito to test functions or classesthat depend on web services or databases. This is only a short introduction tothe Mockito library and the concept of mocking. For more information,see the documentation provided by theMockito package.