SaFi Bank Space : Integration Testing

Overview

Flutter integration test is UI test flow of the app from the user perspective. We use integration_test package that enables self-driving testing of Flutter code on devices and emulators. It adapts flutter_test results into a format that is compatible with flutter drive and native Android instrumentation testing. We are going to execute them locally and also in a more production-grade setup with Firebase Test Lab and Google Cloud.

Setting up the project

To start, add the bdd_widget_test , integration_test , build_runner , and flutter_test package to your pubspec.yaml file :

dependencies:
  bdd_widget_test: ^1.2.1
  integration_test:
    sdk: flutter
  
dev_dependencies:
  build_runner: ^2.1.7
  flutter_test:
    sdk: flutter

Create a build.yaml file in your project :

targets:
  $default:
    sources:
      - integration_test/**
      - lib/**
      - $package$
    builders:
      bdd_widget_test|featureBuilder:
        generate_for:
          - integration_test/**/*.feature
        options:
          stepFolderName: ../step

Driver Entrypoint

Create a folder called integration_test at the root of the Flutter project and inside it a file called driver.dart. This will run the integrationDriver.

import 'package:integration_test/integration_test_driver.dart';

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

Then, create a folder named feature and step in integration_test folder, and inside the step folder create a file named the_app_is_running.dart . For example :

Writing integration tests

Start create a feature file in the feature folder. For example :


After that run this command to generate the test file in your terminal :

flutter pub run build_runner build --delete-conflicting-outputs --build-filter integration_test/**/*_test.dart

It will automate created the test file going to be called <name>_test.dart . For example :

Using Flutter Driver to Run Tests

Just start up an emulator/simulator or even connect a real device through a USB and then run the following command :

flutter drive --driver=integration_test/<name_file_test_driver>.dart --target=integration_test/feature/<name_file_test.dart

For example :

flutter drive --driver=integration_test/common/driver.dart --target=integration_test/feature/demo_page/subcription_demo_page_test.dart

After running this command, you should see something similar on your emulator and also an output saying "All tests passed!" in the terminal.

Testing Android On Firebase Test Lab

Android Setup

  1. Create an instrumentation test file in android/app/src/androidTest/java/com/example/myapp/ directory(replacing com, example, and myapp from app’s package name). You can get app’s package name from file MainActivity.kt

    So, you have to make test file in android/app/src/androidTest/java/com/vl/safi/flutter_safi directory. You can name this test file MainActivityTest.java

    // package com.example.myapp;
    package com.vl.safi.flutter_safi;
    
    import androidx.test.rule.ActivityTestRule;
    import dev.flutter.plugins.integration_test.FlutterTestRunner;
    import org.junit.Rule;
    import org.junit.runner.RunWith;
    
    @RunWith(FlutterTestRunner.class)
    public class MainActivityTest {
      @Rule
      public ActivityTestRule<MainActivity> rule = new ActivityTestRule<>(MainActivity.class, true, false);
    }
  2. Update your application's myapp/android/app/build.gradle

    {
      "consumer": {
        "name": "SafiMobile-Consumer"
      },
      "interactions": [
        {
          "description": "",
          "providerStates": [
            {
              "name": "Get All Card By CustomerId"
            }
          ],
          "request": {
            "method": "GET",
            "path": "/cards/8f7f8a2c-14a3-422d-b1dd-6abc6325597d"
          },
          "response": {
            "body": [
              {
                "accountNo": "0190239038",
                "accountType": "MAIN_ACCOUNT",
                "cardId": "7ea584a2-08b7-44ec-b152-d56a75d96386",
                "cardType": "DP1",
                "customerId": "8f7f8a2c-14a3-422d-b1dd-6abc6325597d",
                "embossName": "Maria Santos Dela Crup",
                "orderingNumber": 1
              }
            ],
            "headers": {
              "Content-Type": "application/json"
            },
            "matchingRules": {
              "body": {
                "$": {
                  "combine": "AND",
                  "matchers": [
                    {
                      "match": "type",
                      "min": 1
                    }
                  ]
                },
                "$[*].accountNo": {
                  "combine": "AND",
                  "matchers": [
                    {
                      "match": "type"
                    }
                  ]
                },
                "$[*].accountType": {
                  "combine": "AND",
                  "matchers": [
                    {
                      "match": "type"
                    }
                  ]
                },
                "$[*].cardId": {
                  "combine": "AND",
                  "matchers": [
                    {
                      "match": "regex",
                      "regex": "^[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}$"
                    }
                  ]
                },
                "$[*].cardType": {
                  "combine": "AND",
                  "matchers": [
                    {
                      "match": "type"
                    }
                  ]
                },
                "$[*].customerId": {
                  "combine": "AND",
                  "matchers": [
                    {
                      "match": "type"
                    }
                  ]
                },
                "$[*].embossName": {
                  "combine": "AND",
                  "matchers": [
                    {
                      "match": "regex",
                      "regex": "^[a-zA-Z ]*$"
                    }
                  ]
                },
                "$[*].expiration": {
                  "combine": "AND",
                  "matchers": [
                    {
                      "match": "type"
                    }
                  ]
                },
                "$[*].internationalBlockingFlag": {
                  "combine": "AND",
                  "matchers": [
                    {
                      "match": "type"
                    }
                  ]
                },
                "$[*].maskingCardNo": {
                  "combine": "AND",
                  "matchers": [
                    {
                      "match": "type"
                    }
                  ]
                },
                "$[*].namePrinted": {
                  "combine": "AND",
                  "matchers": [
                    {
                      "match": "type"
                    }
                  ]
                },
                "$[*].orderingNumber": {
                  "combine": "AND",
                  "matchers": [
                    {
                      "match": "integer"
                    }
                  ]
                },
                "$[*].updatedAt": {
                  "combine": "AND",
                  "matchers": [
                    {
                      "match": "type"
                    }
                  ]
                }
              }
            },
            "status": 200
          }
        }
      ],
      "metadata": {
        "pactRust": {
          "version": "0.1.2"
        },
        "pactSpecification": {
          "version": "3.0.0"
        }
      },
      "provider": {
        "name": "CardManager-Provider"
      }
    }
    

Firebase Test Lab

To using Firebase Test Lab, we can upload Android APK by manual upload or using command line. In this project we will use command line to upload Android APK for testing by Firebase Test Lab.

Create file android_integration.sh to build an instrumentation test for Android use gradle commands

pushd android
# flutter build generates files in android/ for building the app
flutter build apk
./gradlew app:assembleAndroidTest
./gradlew app:assembleDebug -Ptarget=integration_test/feature/safi_demo_test.dart
popd

Google Cloud Setup

  1. To send this apk to firebase automatically, we have to install Google Cloud SDK. Use this guide to install https://cloud.google.com/sdk/docs/install

  2. Go to your Google Cloud console using google account used to access the Firebase Test Lab

  3. Select your project and open Service Account from IAM & Admin menu

  4. Find firebase-adminsdk from the list

  5. In Action, go to manage key and generate the key in json format

  6. Save the file in the roof of the application

  7. Ensure the key is not publish

  8. Go to IAM menu, and find firebase-adminsdk

  9. If you don’t have editor role, you can edit and add role

  10. Go to library from APIs & Services menu

  11. Find Cloud Tool Results API and ensure the API is enable

Finally, update the android_integration.sh

...

gcloud auth activate-service-account --key-file=simple-integration-d61f59baaea2.json

gcloud --quiet config set project simple-integration

gcloud firebase test android run --type instrumentation \
    --app build/app/outputs/apk/debug/app-debug.apk \
    --test build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk \
  • To authenticate the google cloud with service account we create, use key in –key-file=<KEY_FILE>

  • Set the project to firebase project ID. You can get Project ID in Project settings

  • Set firebase test with gcloud from common line for instrumentation test

  • Run ./android_integration.sh

  • You can see the result from firebase test lab

Reference:

  1. Flutter Integration Testing

  2. Running Flutter Integration Test in the Cloud

Attachments:

Screen Shot 2022-04-25 at 08.45.35.png (image/png)
Screen Shot 2022-04-25 at 09.03.24.png (image/png)
eFwKRkj1R5w-OlAKqX_RFW4pcj-Drm5LOqaW9q8rxsnIipz8T1R70MZnTUuV9DTI68BkdsXB9PhBN9iJsumZtlrSdmhoWllNlBttRcSYF5_0jPPPFbHT26G8kBQomFvGIs50wmK5 (image/png)
QCjdwD8EVM9Be73hIdg1PUENe5jeofFWb3DTCEc0s8N1srF6PhSJJIXJjR4g-8nv7bkwYpTw3mzFT17771JccsFqOVjLf9vNLeCG6vPowYQDn4C0N1rc_MJAXgQD7yjkNBf180cF (image/png)
QIlY5O_T4ahWJg2HBkzF9pULRmmbNfOOvDYAYChkVVY2CNm3xJGfdePippoC3LUTwHfXujDxFfT-2M-uSWJjzgi4CDwRayvo7EAGUbNG6my8Oca5eRWJieEicib3fDXYFam1zcQG (image/png)
uF-jxh5gb_RvOzajElPjg4cvHpQOxdtIvyBxe2BnZ81yr_9oCqJ85H9EyxhhSAxaH5XiCqiZN9vHjtAB9sMCA9N_jsRTPQMTiHnarDnGml2TsEcjw28uVfV3Vn4XeZgzCG5304nO (image/png)
Hl6k90AEmm2ojAS5C1hQFR64ErbBlldw1Es8P-2TEDuqtMNlFreV_hWNFSBy_g0CsxciE9jvJUs_2mxPDHoMEdxVF6mitqtQHbpF0JwhzU9B1UpaazaHK_n3j5PmcWaAazpUW2Ze (image/png)
Hl6k90AEmm2ojAS5C1hQFR64ErbBlldw1Es8P-2TEDuqtMNlFreV_hWNFSBy_g0CsxciE9jvJUs_2mxPDHoMEdxVF6mitqtQHbpF0JwhzU9B1UpaazaHK_n3j5PmcWaAazpUW2Ze (image/png)
ouDJt6NBPIlIG91YsrTIpRGaPH5Jdn1OMMkpS1m30LnpW5zbX6f_BIoCT8BRB38f18zHYwkrxm-8x1kFBWFgYYhCQgDdnK-hmWWHOSdTRTMKGi61v3i5rkBJNjd6pQKRhYj27XdI (image/png)
Y4ZMa7KbDu292aC_dp6y6liqyvg0T67TrXvLWj1x1uY5-adU66OmhwmdZQ4rY570UWghegXqyMf3uZuaN0xur51i7UbTTU1uq4YF2xVXhIDoBpeCWnVs6xZ4njLaumaXzTp3L34M (image/png)
0JgH6jcBdGmaXD_ZPXh9MBMl_ooXByy5cUOjcj1HiZfOnpPauarS0QnlpS8x3sB9qnCiQy1Q2UGYFC2ei-r9GdlXFEx-4IRjP1b_87tHMdjhjv8yJVuIl-S-Epqc3LvTTPsduzeW (image/png)
38rAs09_8dG9wNRETD0KyQDZzOX9YRUFnNjWhfC4bJ_GhlEzisIrz4l5-IbNQ7MjWCQBRLzdfnEEKyWIp1q5HEtj0N21Ip3A4iFggyvz3LbQkOgVA1-iLfJPaXEx_GZdW9uBVGe9 (image/png)
7IdscwyQs4JDugCjA2TF8Rx2GgtbN3l2S_KzLfFSRfWS6qBtwHjFPe5vOWe-03AUDfVyik5rr3X-13fJl8VXLxhYNCMOu7SeH--5fOPI9BaefnYUZdkdTClBp9UsrNzaoCqlam4m (image/png)
i1op_nx5S09b98OqQWWyC1-I2ZL99YEpEghDstslxbUAovt0SZWadslMM0Im739RJCEfhJKI2vTbCcFlAQ5Unv3fn_GUbBcimxqDNdS7XDuscvrHjFQkBrulPaAFNX5bsbWu5Ftm (image/png)
UNLT1IWtIAbMW5Uvm9EIXfMVK535uvYIs1dpZBEe8co8_hifrurFVE-ia1N6bFMzuqNO9KY5ycpc82fygndDtzHeuVPDlcds0sPZ-DvIU5iHgqrddg66cug8QOQtzNnlUmK6m2Kj (image/png)
JaauWQV1EPkuSPF9iUWRiMwU0zjtU5OSgySM-gfEVB7DLk7B81BEfJrVY0NynS5l0-DYPGkHxj6wHD24QKmL4CkrUF0OzHB_QQSct35EeXIzHJp_4Pw5PLQuDz6n4dCLGzgriOoV (image/png)
pYcNSl4_uKTKcfYo-8Pp3pYY5Earc4EB7g-aHqmDc74h97Y62wA6KUROT7gS6gkkGPUDHTASVAXxvoWkb5cabRdI4luLqaCri21vSHtL69Pw-pVjnDYJlNvVhrDAr0xtvUtKKr1T (image/png)
9Aht0Zl3wEtdM0twb-v5HWCcgz5KnwYtKu9y6ax4Q86qa9bPyxlDCtm3HW_jBl2EljdPLAqjckOcAuq2UMBh8KAfgq4BIICMCP07o2qXeuh1y9eQ6Ig7pALtAH8Kwqx7O6FO-B9D (image/png)
taAq9jrD0ms4O8QsRTTJz-29hBI-CNTMPNP-EYARVPwfQ6j6AF6C16UXaQdwjoKOwnMe2gapuwrCg_MilM-6oy-x_3jAvpM2W_51uPMnnE82C1aUkFYLq-azCjr2MmxXUfQpEqxH (image/png)
3e5OpJvbvOPq0j3XFs4mMyYBHmAje_czhFEyfa5xcj6vn8T651Cnt5Sfy-ZpP8FBOpVMxozjo2D1kqAvRhhpPvSeP3i3p2JpRAQ9DXhAXaHXiZlU2XMk2BjVQ0VhnjrUbCND8_n6 (image/png)
iwvtEuECe3aJg_NlItBksMAE51JOqHco7dcO1ORty7vU_XmydMwPlX0fcL2vgRX2e6yr4zA_P9F_FcovLDyIm8COF3OwmLT1e2cBrKxdiA2O2B3mmo6ZGusXsEh65l_XUQrDJXUz (image/png)
Screen Shot 2022-06-27 at 12.21.20.png (image/png)
Screen Shot 2022-06-27 at 12.21.55.png (image/png)
Screen Shot 2022-06-27 at 12.22.33.png (image/png)
Screen Shot 2022-06-27 at 12.36.47.png (image/png)