Integration Testing in Flutter
Tags: computerscience, productivity, flutter, programming
Published: 2021-10-13 15:02:03

*In case it helped :)*
<a href="https://www.buymeacoffee.com/aseemwangoo" target="_blank"><img src="https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png" alt="Pass Me A Coffee!!" style="height: 41px !important;width: 174px !important;box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;-webkit-box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;" ></a>

<!-- wp:paragraph -->
<p>We will cover briefly:</p>
<!-- /wp:paragraph -->

<!-- wp:list {"ordered":true} -->
<ol><li>Setup for integration test flutter</li><li>Using Robot pattern for testing</li><li>Test the app</li><li>(Optional) Recording performance</li></ol>
<!-- /wp:list -->

<!-- wp:quote -->
<blockquote class="wp-block-quote"><p></p></blockquote>
<!-- /wp:quote -->

{% youtube 9F6LkvvTTl8 %}

<!-- wp:heading {"level":3} -->
<h3>Setup for integration tests</h3>
<!-- /wp:heading -->

{% youtube UQ4HIzgwxlM %}

<!-- wp:paragraph -->
<p>Integration testing (also called end-to-end testing or GUI testing) is used to simulate a user interacting with your app by doing things like clicking buttons, selecting items, scrolling items, etc.&nbsp;</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>We use the <code>integration_test</code> package for writing integration tests. Add the plugin to your <code>pubspec.yaml</code> file as a development dependency</p>
<!-- /wp:paragraph -->

```dart
# pubspec.yaml
dev_dependencies:
  flutter_test:
    sdk: flutter
  integration_test
    sdk: flutter
```

<!-- wp:quote -->
<blockquote class="wp-block-quote"><p>Detailed description <a href="https://flutter.dev/docs/testing/integration-tests" rel="noreferrer noopener" target="_blank">here</a>.</p></blockquote>
<!-- /wp:quote -->

<!-- wp:paragraph -->
<p><strong>Begin Code</strong></p>
<!-- /wp:paragraph -->

{% youtube hhjSTJOJ-2g %}

<!-- wp:image {"align":"center"} -->
<div class="wp-block-image"><figure class="aligncenter"><img src="https://cdn-images-1.medium.com/max/1600/1*QcN7y0Kd5GuKKPe3aELTBQ.png" alt="Summary for Integration Tests"/><figcaption>Summary for Integration Tests</figcaption></figure></div>
<!-- /wp:image -->

<!-- wp:list -->
<ul><li>We create a new directory <code>test_driver</code>containing a new file, <code><a href="https://github.com/AseemWangoo/dynamism/blob/master/test_driver/integration_test.dart" rel="noreferrer noopener" target="_blank">integration_test.dart</a></code></li><li>We need to call <code>integrationDriver()</code> inside this file, but we will customize it, for allowing us to save the results of the integration tests.</li></ul>
<!-- /wp:list -->

```dart
## Save the results of the integration tests
integrationDriver(responseDataCallback:(Map&lt;String, dynamic> data) 
async {
  
  await fs.directory(_destinationDirectory).create(recursive: true);
  final file = fs.file(path.join(
      _destinationDirectory,
      '$_testOutputFilename.json',
  ));
  final resultString = _encodeJson(data);
  await file.writeAsString(resultString);
});
const _testOutputFilename = 'integration_response_data';
```

<!-- wp:quote -->
<blockquote class="wp-block-quote"><p>This will create a file <code>integration_response_data.json</code> after our tests are finished.</p></blockquote>
<!-- /wp:quote -->

<!-- wp:image {"align":"center"} -->
<div class="wp-block-image"><figure class="aligncenter"><img src="https://cdn-images-1.medium.com/max/1600/1*B-KJ2h2Pb8AFEbypEbugcA.png" alt=""/><figcaption>Integration Test Results</figcaption></figure></div>
<!-- /wp:image -->

<!-- wp:paragraph -->
<p>Next, we write tests under <code><a href="https://github.com/AseemWangoo/dynamism/blob/master/integration_test/app_test.dart" rel="noreferrer noopener" target="_blank">integration_test/app_test.dart</a></code> We need to call the method <code>IntegrationTestWidgetsFlutterBinding.ensureInitialized()</code> in the main function.</p>
<!-- /wp:paragraph -->

<!-- wp:quote -->
<blockquote class="wp-block-quote"><p>Note: The tests should follow <code>integration_test/&lt;name&gt;_test.dart</code> pattern</p></blockquote>
<!-- /wp:quote -->

<!-- wp:list -->
<ul><li>To run our integration tests, first, make sure the emulators are running, and then we run the following</li></ul>
<!-- /wp:list -->

```dart
flutter drive \
  --driver=test_driver/integration_test.dart \
  --target=integration_test/app_test.dart
## To specify a device
flutter drive \
  --driver=test_driver/integration_test.dart \
  --target=integration_test/app_test.dart
  -d "9B4DC39F-5419-4B26-9330-0B72FE14E15E"
## where 9B4DC39F-5419-4B26-9330-0B72FE14E15E is my iOS simulator
```

<!-- wp:paragraph -->
<p><strong>Using Robot pattern for testing</strong></p>
<!-- /wp:paragraph -->

{% youtube A5lcduAfGDo %}

<!-- wp:paragraph -->
<p>Robot Testing is an end-to-end (E2E) technique that mimics human interaction. It focuses on <strong>WHAT WE ARE TESTING</strong> instead of <strong>HOW WE ARE TESTING.</strong></p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Instead of writing all the tests in one file, we basically create robots per screen and then use the integration testing tools to test our application.</p>
<!-- /wp:paragraph -->

<!-- wp:quote -->
<blockquote class="wp-block-quote"><p><a href="https://verygood.ventures/blog/robot-testing-in-flutter" rel="noreferrer noopener" target="_blank">Inspired by this</a></p></blockquote>
<!-- /wp:quote -->

<!-- wp:image {"align":"center"} -->
<div class="wp-block-image"><figure class="aligncenter"><img src="https://cdn-images-1.medium.com/max/1600/1*7AtZPVa8x6fopJFYOBdJ7w.png" alt="Robot pattern for testing"/><figcaption>Robot pattern for testing</figcaption></figure></div>
<!-- /wp:image -->

<!-- wp:heading {"level":4} -->
<h4>Create a&nbsp;Robot</h4>
<!-- /wp:heading -->

<!-- wp:list -->
<ul><li>We create a new file called <code><a href="https://github.com/AseemWangoo/dynamism/blob/master/integration_test/robots/home_robot.dart" rel="noreferrer noopener" target="_blank">home_robot.dart</a></code> which takes in the <a href="https://api.flutter.dev/flutter/flutter_test/WidgetTester-class.html" rel="noreferrer noopener" target="_blank">WidgetTester</a> as a constructor parameter.&nbsp;</li></ul>
<!-- /wp:list -->

<!-- wp:quote -->
<blockquote class="wp-block-quote"><p>Note: Every robot takes in the WidgetTester as a constructor parameter and defines the methods as required by that screen</p></blockquote>
<!-- /wp:quote -->

```dart
class HomeRobot {
  const HomeRobot(this.tester);
  final WidgetTester tester;
  Future<void> findTitle() async {}
  Future<void> scrollThePage({bool scrollUp = false}) async{}           
  Future<void> clickFirstButton() async {}
  Future<void> clickSecondButton() async {}
}
```

<!-- wp:paragraph -->
<p>and we have the methods declared as required per our home screen.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Inside our, <code><a href="https://github.com/AseemWangoo/dynamism/blob/master/integration_test/app_test.dart" rel="noreferrer noopener" target="_blank">app_test.dart</a></code> we initialize the HomeRobot inside the<code>testWidgets</code> function and then use the methods as per the behavior</p>
<!-- /wp:paragraph -->

<!-- wp:image {"align":"center"} -->
<div class="wp-block-image"><figure class="aligncenter"><img src="https://cdn-images-1.medium.com/max/1600/1*vH-FTb6IYbvl8fq7w3tckg.png" alt="Home Robot example"/><figcaption>Home Robot example</figcaption></figure></div>
<!-- /wp:image -->

<!-- wp:list -->
<ul><li>Even the non-technical stakeholders can understand the tests here, which is the point for robot testing.</li><li>We repeat the steps for the second and third screens, by creating screen respective robots and writing tests inside the main file.</li></ul>
<!-- /wp:list -->

<!-- wp:paragraph -->
<p><strong>Test the app</strong></p>
<!-- /wp:paragraph -->

{% youtube 7pwrgfZ7LJs %}

<!-- wp:image {"align":"center"} -->
<div class="wp-block-image"><figure class="aligncenter"><img src="https://cdn-images-1.medium.com/max/1600/1*M2ZDK4yyLTIhfLs4Xre55w.png" alt="Home Screen"/><figcaption>Home Screen</figcaption></figure></div>
<!-- /wp:image -->

<!-- wp:paragraph -->
<p>This is our home screen, we write the first test for finding the title.</p>
<!-- /wp:paragraph -->

<!-- wp:heading {"level":4} -->
<h4>Find title</h4>
<!-- /wp:heading -->

<!-- wp:list -->
<ul><li>We make use of the tester inside the home robot and call <code>pumpAndSettle</code> </li><li>Finally, we expect to find the text (on the home screen) and only with one widget.</li></ul>
<!-- /wp:list -->

```dart
Future<void> findTitle() async {
  await tester.pumpAndSettle();
  expect(find.text("Fames volutpat."), findsOneWidget);
}
```

<!-- wp:quote -->
<blockquote class="wp-block-quote"><p>Note: <code><a href="https://api.flutter.dev/flutter/flutter_test/WidgetTester/pumpAndSettle.html" rel="noreferrer noopener" target="_blank">pumpAndSettle</a></code> triggers the frames, until there is nothing to be scheduled</p></blockquote>
<!-- /wp:quote -->

<!-- wp:heading {"level":4} -->
<h4>Scroll the&nbsp;page</h4>
<!-- /wp:heading -->

<!-- wp:list -->
<ul><li>We specify the finder (for the scroll widget) and call the <code><a href="https://api.flutter.dev/flutter/flutter_test/WidgetController/fling.html" rel="noreferrer noopener" target="_blank">fling</a></code> method on the tester. </li><li>In order to scroll down, we need to specify y-offset as negative and for scrolling up, vice-versa</li></ul>
<!-- /wp:list -->

```dart
Future<void> scrollThePage({bool scrollUp = false}) async {
  final listFinder = find.byKey(const Key('singleChildScrollView'));
  if (scrollUp) {
    await tester.fling(listFinder, const Offset(0, 500), 10000);
    await tester.pumpAndSettle();
    expect(find.text("Fames volutpat."), findsOneWidget);
  } else {
    
    await tester.fling(listFinder, const Offset(0, -500), 10000);
    await tester.pumpAndSettle();
    expect(find.text("Sollicitudin in tortor."), findsOneWidget);
  }
}
```

<!-- wp:paragraph -->
<p>Once the scrolling is done, we expect to find the text at the bottom</p>
<!-- /wp:paragraph -->

<!-- wp:image {"align":"center"} -->
<div class="wp-block-image"><figure class="aligncenter"><img src="https://cdn-images-1.medium.com/max/1600/1*hRHgAVdbx_a41Wf8ZGsDaQ.png" alt="HomeScreen bottom part"/><figcaption>HomeScreen bottom part</figcaption></figure></div>
<!-- /wp:image -->

<!-- wp:heading {"level":4} -->
<h4>Click the&nbsp;button</h4>
<!-- /wp:heading -->

<!-- wp:list -->
<ul><li>After reaching the bottom of the home screen, now we simulate the button press.</li><li>First, we make sure that the button is visible, by calling<code><a href="https://api.flutter.dev/flutter/flutter_test/WidgetController/ensureVisible.html" rel="noreferrer noopener" target="_blank">ensureVisible</a></code> </li><li>Finally, we simulate the button press using <code><a href="https://api.flutter.dev/flutter/flutter_test/WidgetController/tap.html" rel="noreferrer noopener" target="_blank">tap</a></code></li></ul>
<!-- /wp:list -->

```dart
Future<void> clickFirstButton() async {
  final btnFinder = find.byKey(const Key(HomeStrings.bOp1));
  await tester.ensureVisible(btnFinder);
  await tester.tap(btnFinder);
  await tester.pumpAndSettle();
}
```

<!-- wp:list -->
<ul><li>Once, the buttons are pressed we are navigated to the next screens. Let’s test those.</li></ul>
<!-- /wp:list -->

<!-- wp:heading {"level":4} -->
<h4>Second Screen</h4>
<!-- /wp:heading -->

<!-- wp:list -->
<ul><li>As mentioned above, we follow the robot pattern, hence we have the <code><a href="https://github.com/AseemWangoo/dynamism/blob/master/integration_test/robots/secondscreen_robot.dart" rel="noreferrer noopener" target="_blank">secondscreen_robot.dart</a></code> </li><li>We find the title, scroll the page (same as above), additionally now we test the back button.</li><li>We navigate back to the home screen using <code><a href="https://api.flutter.dev/flutter/flutter_driver/PageBack-class.html" rel="noreferrer noopener" target="_blank">pageBack</a></code> (this finds the back button on the scaffold)</li></ul>
<!-- /wp:list -->

```dart
Future<void> goBack() async {
  await tester.pageBack();
  await tester.pumpAndSettle();
}
```

<!-- wp:heading {"level":4} -->
<h4>Third Screen</h4>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>Below is our third screen</p>
<!-- /wp:paragraph -->

<!-- wp:image {"align":"center"} -->
<div class="wp-block-image"><figure class="aligncenter"><img src="https://cdn-images-1.medium.com/max/1600/1*gVmbvssXOnFg5rqJUJDWoA.png" alt="Third Screen"/><figcaption>Third Screen</figcaption></figure></div>
<!-- /wp:image -->

<!-- wp:list -->
<ul><li>We follow the robot pattern, hence we have the <code><a href="https://github.com/AseemWangoo/dynamism/blob/master/integration_test/robots/thirdscreen_robot.dart" rel="noreferrer noopener" target="_blank">thirdscreen_robot.dart</a></code> </li><li>We find the title, scroll the page (same as above), additionally now we perform the click action on the tile using <code><a href="https://api.flutter.dev/flutter/flutter_test/WidgetController/tap.html" rel="noreferrer noopener" target="_blank">tap</a></code></li></ul>
<!-- /wp:list -->

```dart
Future<void> clickTile(int item) async {
  assert(item != null &amp;&amp; item >= 0 &amp;&amp; item &lt;= 5);
  final key = 'fringilla_item_${item.toString()}';
  final itemFinder = find.byKey(Key(key));
  await tester.tap(itemFinder);
  await tester.pumpAndSettle();
}
```

<!-- wp:paragraph -->
<p>With the click of the tile, we open a web view with a URL, but now we have a cross icon.</p>
<!-- /wp:paragraph -->

<!-- wp:image {"align":"center"} -->
<div class="wp-block-image"><figure class="aligncenter"><img src="https://cdn-images-1.medium.com/max/1600/1*KL9yGwP9hgsyBUlPndkqTQ.png" alt="WebView Flutter"/><figcaption>WebView Flutter</figcaption></figure></div>
<!-- /wp:image -->

<!-- wp:list -->
<ul><li>We find the cross icon, using <code>find.byIcon</code> and then use tap to close the current webview.</li></ul>
<!-- /wp:list -->


```dart
Future<void> goBack() async {
   final closeIconFinder = find.byIcon(Icons.close);
   await tester.tap(closeIconFinder);
   await tester.pumpAndSettle();
   await tester.pageBack();
   await tester.pumpAndSettle();
}
```

<!-- wp:paragraph -->
<p>Now, we come to the third screen and from there, we call<code><a href="https://api.flutter.dev/flutter/flutter_driver/PageBack-class.html" rel="noreferrer noopener" target="_blank">pageBack</a></code> to return to the home screen.</p>
<!-- /wp:paragraph -->

<!-- wp:heading {"level":3} -->
<h3>Recording Performance</h3>
<!-- /wp:heading -->

<!-- wp:list -->
<ul><li>Let’s say you want to record the performance of your test (maybe scroll) we first make use of <code>IntegrationTestWidgetsFlutterBinding.ensureInitialized()</code> </li><li>Then we call the <code><a href="https://api.flutter.dev/flutter/package-integration_test_integration_test/IntegrationTestWidgetsFlutterBinding/watchPerformance.html" rel="noreferrer noopener" target="_blank">watchPerformance</a></code> and inside it, we perform our scroll</li></ul>
<!-- /wp:list -->

```dart
final listFinder = find.byKey(const Key('singleChildScrollView'));
await binding.watchPerformance(() async {
  await tester.fling(listFinder, const Offset(0, -500), 10000);
  await tester.pumpAndSettle();
});
```

<!-- wp:quote -->
<blockquote class="wp-block-quote"><p>Note: The result is saved under the JSON file (which we created during the initialization of integration tests)</p></blockquote>
<!-- /wp:quote -->

<!-- wp:paragraph -->
<p><a rel="noreferrer noopener" href="https://github.com/AseemWangoo/dynamism" target="_blank"><em>Source code.</em></a>

{% youtube 9F6LkvvTTl8 %}

*In case it helped :)*
<a href="https://www.buymeacoffee.com/aseemwangoo" target="_blank"><img src="https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png" alt="Pass Me A Coffee!!" style="height: 41px !important;width: 174px !important;box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;-webkit-box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;" ></a>