Writing Integration Test In Flutter

Writing Integration Test In Flutter

Integration tests in flutter allows us to test a complete app or a large part of an app in other to confirm how individual pieces work together as a whole, or capture the performance of an application running on a real device checking to make sure everything is correct along the way.

So while unit tests and widget tests are handy for testing individual classes, functions, or widgets. Integration test are able to test the app like it is being operated by a user. For example, with integration test you would be able to interact with external resources (e.g. network calls and database) and navigating between screens.

My previous article covered how to write widget test in flutter. In this article we are going to add integration test to our flutter app.

The Setup

To get started, simply clone the example project from github here and run flutter pub get.

git clone https://github.com/o-ifeanyi/test-sample

flutter pub get

Add the integration_test dependency to the pubspec.yaml file

dev_dependencies:
  integration_test:
    sdk: flutter
  flutter_test:
    sdk: flutter
  mockito: ^4.1.3

Create a test_driver folder at the root of your project and within it create a file named app.dart. Add the following to app.dart file. This is used to run/drive the app on an emulator or real device.

import 'package:integration_test/integration_test_driver.dart';

Future<void> main() => integrationDriver();

Crete an integration_test folder at the root of your project and within it create a file named joke_test.dart. This is where well be writing the integration test. In this case the test would simply start the app, tap on the Get Joke button and expect to find a joke on the screen after its done loading.

The folder structure currently looks like this

lib/
...
integration_test/
    joke_test.dart
test_driver/
    app.dart

Writing The Test

Writing integration test is very similar to writing widget test in the sense that it makes use of the 'testWidgets' function which automatically creates a new WidgetTester for each test case. This enables us build and interact with widgets in a test environment.

import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';

void main() {
  IntegrationTestWidgetsFlutterBinding.ensureInitialized();

  testWidgets("test description", (WidgetTester tester) async {
    // ...
  });
}

So first thing to do is find the widget well be interacting with. in this case the Get Joke button.

final getJokeButton = find.byKey(ValueKey('getJokeButton'));

Next we'll render the UI on the screen using the pumpWidget function

await tester.pumpWidget(
  MaterialApp(
    home: MyHomePage(api: Api(client: Client())),
  ),
);

Then we tap on the button and wait for the loading to complete.

await tester.tap(getJokeButton);

await tester.pumpAndSettle();

Since a loading spinner is displayed while the api call is being processed, with the pumAndSettle function we basically keep refreshing the screen until there are no more changes or animations left to render on the screen. At the end of the load we expect to find a joke on the screen

expect(find.textContaining("HERE'S THE JOKE"), findsOneWidget);

Within the api.dart file. When a joke is returned it comes in this format.

if (response.statusCode == 200) {
   final joke = json.decode(response.body);
   return "HERE'S THE JOKE\n${joke['setup']}\n${joke['punchline']}";
}

Which is why we can expect to find a text that constains "HERE'S THE JOKE"

The complete joke_test.dart file looks like this

import 'package:example/api.dart';
import 'package:example/main.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:http/http.dart';
import 'package:integration_test/integration_test.dart';

void main() {
  IntegrationTestWidgetsFlutterBinding.ensureInitialized();

  testWidgets("Get joke test", (WidgetTester tester) async {
    final getJokeButton = find.byKey(ValueKey('getJokeButton'));
    await tester.pumpWidget(
      MaterialApp(
        home: MyHomePage(api: Api(client: Client())),
      ),
    );

    await tester.tap(getJokeButton);
    await tester.pumpAndSettle();

    expect(find.textContaining('HERE\'S THE JOKE'), findsOneWidget);
    await Future.delayed(Duration(seconds: 2));
  });
}

Running The Test

To run all the test, simply make sure you're within the app's directory (e.g. C:\Users\ifeanyi\FlutterProjects\example>) and run flutter drive --driver=driver/location --target=test/location.

flutter drive --driver=test_driver/app.dart --target=integration_test/joke_test.dart

gif-sample.gif

That'll be all for now. In the next article we'll clean up by using Get_it to register classes instead of passing it around. Stay tuned.

Thanks for reading

For more information on how to write integration test, see the documentation provided by the integration test package.

The flutter docs on integration test is also a good reference

Check out the full code here