Open In App

How to Write TestCases For API Calls in Flutter?

Improve
Improve
Like Article
Like
Save
Share
Report

Here we are going to a built app that calls an API and write test cases for it before we dive into it let’s go through some basic concepts.

Software testing is a process in which we test code to ensure that it produces the excepted results at any given instance.

Flutter tests consist of:

  1. Unit test – test for methods, functions, or class
  2. Widget test – test for a single widget
  3. Integration test – test for the large part of or the entire application

As we are going to test a function that calls the API we’ll be doing unit testing. Let’s create the app. You can create the app using the flutter create command or use the IDE of your choice. In the app, we’ll use the Numbers API which will give random trivia about numbers, and then will test the function that makes the API call. We are assuming you have some sort of experience with flutter app development.

First, we’ll add the http dependency in the pubspec.yaml file and pub get the dependencies.

XML




name: flutter_unit_test
description: A new Flutter project.
  
# The following line prevents the package from being accidentally published to
# pub.dev using `flutter pub publish`. This is preferred for private packages.
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
  
# The following defines the version and build number for your application.
# A version number is three numbers separated by dots, like 1.2.43
# followed by an optional build number separated by a +.
# Both the version and the builder number may be overridden in flutter
# build by specifying --build-name and --build-number, respectively.
# In Android, build-name is used as versionName while build-number used as versionCode.
# Read more about Android versioning at https://developer.android.com/studio/publish/versioning
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
# Read more about iOS versioning at
version: 1.0.0+1
  
environment:
  sdk: ">=2.12.0 <3.0.0"
  
# Dependencies specify other packages that your package needs in order to work.
# To automatically upgrade your package dependencies to the latest versions
# consider running `flutter pub upgrade --major-versions`. Alternatively,
# dependencies can be manually updated by changing the version numbers below to
# the latest version available on pub.dev. To see which dependencies have newer
# versions available, run `flutter pub outdated`.
dependencies:
  flutter:
    sdk: flutter
  http: 0.13.4
  
  
  # The following adds the Cupertino Icons font to your application.
  # Use with the CupertinoIcons class for iOS style icons.
  cupertino_icons: ^1.0.2
  
dev_dependencies:
  flutter_test:
    sdk: flutter
  
  # The "flutter_lints" package below contains a set of recommended lints to
  # encourage good coding practices. The lint set provided by the package is
  # activated in the `analysis_options.yaml` file located at the root of your
  # package. See that file for information about deactivating specific lint
  # rules and activating additional ones.
  flutter_lints: ^1.0.0
  
# For information on the generic Dart part of this file, see the
  
# The following section is specific to Flutter.
flutter:
  
  # The following line ensures that the Material Icons font is
  # included with your application, so that you can use the icons in
  # the material Icons class.
  uses-material-design: true
  
  # To add assets to your application, add an assets section, like this:
  # assets:
  #   - images/a_dot_burr.jpeg
  #   - images/a_dot_ham.jpeg
  
  # An image asset can refer to one or more resolution-specific "variants", see
  
  # For details regarding adding assets from package dependencies, see
  
  # To add custom fonts to your application, add a fonts section here,
  # in this "flutter" section. Each entry in this list should have a
  # "family" key with the font family name, and a "fonts" key with a
  # list giving the asset and other descriptors for the font. For
  # example:
  # fonts:
  #   - family: Schyler
  #     fonts:
  #       - asset: fonts/Schyler-Regular.ttf
  #       - asset: fonts/Schyler-Italic.ttf
  #         style: italic
  #   - family: Trajan Pro
  #     fonts:
  #       - asset: fonts/TrajanPro.ttf
  #       - asset: fonts/TrajanPro_Bold.ttf
  #         weight: 700
  #
  # For details regarding fonts from package dependencies,


Now let’s create the app. You can remove all the contents of the main.dart file in the lib folder and remove the file in the test folder. Now we’ll build the app in the main.dart file.

Dart




import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
  
void main() {
  runApp(
    MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text(
            'GeeksForGeeks',
          ),
          backgroundColor: Colors.green,
        ),
        body: const MyApp(),
      ),
    ),
  );
}
  
class MyApp extends StatefulWidget {
  const MyApp({Key? key}) : super(key: key);
  
  @override
  _MyAppState createState() => _MyAppState();
}
  
class _MyAppState extends State<MyApp> {
  @override
  Widget build(BuildContext context) {
    return FutureBuilder(
        future: getNumberTrivia(),
        builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
          if (snapshot.connectionState == ConnectionState.waiting) {
            return const Center(
              child: CircularProgressIndicator(
                color: Colors.green,
              ),
            );
          } else if (snapshot.connectionState == ConnectionState.done) {
            return Padding(
              padding: const EdgeInsets.all(8.0),
              child: Center(
                child: Text(
                  snapshot.data.toString(),
                ),
              ),
            );
          } else {
            return const Center(
              child: Text(
                'Error Occurred',
              ),
            );
          }
        });
  }
}
  
Future<String> getNumberTrivia() async {
  Uri numberAPIURL = Uri.parse('http://numbersapi.com/random/trivia?json');
  final response = await http.get(numberAPIURL);
  if (response.statusCode == 200) {
    final Map triviaJSON = jsonDecode(response.body);
    return triviaJSON['text'];
  } else {
    return 'Failed to fetch number trivia';
  }
}


Run the app using the flutter run command or run button in your IDE.

In order to write a test, we need to understand the method to be tested. Here we have a function named getNumberTrivia that calls an API that returns a JSON response of number trivia upon success otherwise it returns an error message.

Now we can write two test cases first to test whether the successful API response returns a text containing the number trivia and second when the API call fails it returns an error message.

Before we start the test we need to understand that we should not make HTTP requests in the test. It’s not recommended. Instead, we must use mocking or stubs. Luckily the HTTP package provides testing.dart file for us to use.

It is recommended that we create a folder called test at the root of the project and write our tests in that. It is already present in our project when we created it. Make sure that you have flutter_test in your dev dependencies and http in the dependencies section of your pubspec.yaml file.

Before we test the function we should make the http dependency as a parameter to the function it will help us in the mocking and stubs part of the test.

Dart




Future<String> getNumberTrivia(http.Client http) async {
  Uri numberAPIURL = Uri.parse('http://numbersapi.com/random/trivia?json');
  final response = await http.get(numberAPIURL);
  if (response.statusCode == 200) {
    final Map triviaJSON = jsonDecode(response.body);
    return triviaJSON['text'];
  } else {
    return 'Failed to fetch number trivia';
  }
}


Dart




class _MyAppState extends State<MyApp> {
  @override
  Widget build(BuildContext context) {
    return FutureBuilder(
        future: getNumberTrivia(http.Client()),
        builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
          if (snapshot.connectionState == ConnectionState.waiting) {
            return const Center(
              child: CircularProgressIndicator(
                color: Colors.green,
              ),
            );
          } else if (snapshot.connectionState == ConnectionState.done) {
            return Padding(
              padding: const EdgeInsets.all(8.0),
              child: Center(
                child: Text(
                  snapshot.data.toString(),
                ),
              ),
            );
          } else {
            return const Center(
              child: Text(
                'Error Occurred',
              ),
            );
          }
        });
  }
}


Create a dart file you can name it as get_number_trivia_unit_test.dart.Make sure that you put _test at the end of the file name this helps flutter understand the files for the test while using the flutter test command.

Some of the functions to know for the flutter test. These functions come from the flutter_test package.

  • group(description,body); You can group test cases related to a particular function using the group function. You can write the description of the test in the description parameter and the body contains all the test cases.
  • test(description,body); You can write a test case using the test function to which you can provide the description in the parameter and the body will contain the test case itself.
  • expect(actual,matcher); You can test the output by using the expect method by providing it the output of the function as an actual parameter and expected output as a matcher.

There is various matcher you can find in the official documentation.

Dart




import 'dart:convert';
import 'package:flutter_test/flutter_test.dart';
  
// file which has the getNumberTrivia function
import 'package:flutter_unit_test/main.dart';
import 'package:http/http.dart';
import 'package:http/testing.dart';
  
void main() {
  group('getNumberTrivia', () {
    test('returns number trivia string when http response is successful',
        () async {
            
      // Mock the API call to return a json response with http status 200 Ok //
      final mockHTTPClient = MockClient((request) async {
          
        // Create sample response of the HTTP call //
        final response = {
          "text":
              "22834 is the feet above sea level of the highest mountain 
              in the Western Hemisphere, Mount Aconcagua in Argentina.",
          "number": 22834,
          "found": true,
          "type": "trivia"
        };
        return Response(jsonEncode(response), 200);
      });
      // Check whether getNumberTrivia function returns
      // number trivia which will be a String
      expect(await getNumberTrivia(mockHTTPClient), isA<String>());
    });
  
    test('return error message when http response is unsuccessful', () async {
        
      // Mock the API call to return an 
      // empty json response with http status 404
      final mockHTTPClient = MockClient((request) async {
        final response = {};
        return Response(jsonEncode(response), 404);
      });
      expect(await getNumberTrivia(mockHTTPClient),
          'Failed to fetch number trivia');
    });
  });
}


In order to run the test enter the following command in the terminal

flutter test 

Output:



Last Updated : 16 Oct, 2021
Like Article
Save Article
Previous
Next
Share your thoughts in the comments
Similar Reads