Run sample Flutter app test

Why Appium Flutter driver

In general, the Appium Flutter Driver allows you to interact with elements or perform actions like from the original Flutter Driver, but you can write tests on other languages than the Dart. Also, you can run your tests for an App with an embedded webview or native view. At the same time, it’s very easy to run such tests on multiple devices simultaneously. More information can be found here.

Step 1: Prepare Flutter application

The Appium Flutter Driver uses the Dart VM Service Protocol with an extension ext.flutter.driver, similar to Flutter Driver, to control the Flutter app-under-test (AUT). This needs to be enabled before building the App and can be performed by the following steps below:

  1. Open pubspec.yaml file in IDE or any editor.
  2. Add flutter_test and flutter_driver to dev_dependencies:
    dev_dependencies:
      ...
      flutter_test:
        sdk: flutter
      flutter_driver:
        sdk: flutter  
  3. Run flutter pub get command to install dependencies added in the previous step.
  4. Open lib/main.dart and add the following import:
    import 'package:flutter_driver/driver_extension.dart';
  5. As a final step, please add the following call runApp in the method main:
    void main() async {
      enableFlutterDriverExtension();
      WidgetsFlutterBinding.ensureInitialized();
      await configureDependencies();
      runApp(const MyApp());
    }

Step 2: Building the App

After enabling the Flutter Driver extension, you need to build app(s) for testing. You can do this in the following way:

  1. Open your Flutter project in IDE or in the Terminal.
  2. In case of Terminal, please make sure that the flutter command line tool is available (run ‘flutter doctor‘ command to verify)
  3. After that, run one of the following commands:
    Android:
    
    flutter build apk --debug
    
    iOS:
    
    flutter build ipa --profile
  4. Appropriate app files could be found in the following directories:
    Android:
    
    {project-root}/build/app/outputs/flutter-apk/
    
    iOS:
    
    {project-root}/build/ios/ipa/

Step 3: Setup environment for writing tests

Requirements

Step 4: Upload your app

Upload your built app for Android (.apk file) or iOS (.ipa file) to the Mobitru using our REST API. Here is an example cURL request to upload the app:

curl --location --request POST 'https://app.mobitru.com/billing/unit/<BILLING_UNIT>/automation/api/v1/spaces/artifacts' \
--header 'x-File-Name: AppFlutter.apk' \
--header 'X-Content-Type: application/zip' \
--header 'Authorization: Bearer <ACCESS_KEY>' \
--form 'file=@"/path/to/app/file/app-flutter.apk"' \
--form 'checksum="None"'

Below you can find a sample of the response with an id of the uploaded app:

{
    "id": "b7b0aae5-a839-4c15-b6ba-98edb75d07a1",
    "name": "22d69718-59a9-40c6-809c-0e3d4a7313b7.apk",
    "realName": "AppFlutter.apk",
    "bid": "916f0549-4ddc-491a-9fe7-3f27597fd3b7",
    "wid": "0",
    "private": true,
    "uploadedBy": "test_user@epam.com",
    "uploadedAt": 1670264922,
    "verified": false,
    "target": "android",
    "contentType": "application/zip",
    "contentLength": 0,
    "href": "quarantine/22d69718-59a9-40c6-809c-0e3d4a7313b7.apk",
    "checksum": "None",
    "alias": "automation",
    "apk": null,
    "ipa": null
}

Step 5: Find and take a device

Find and take appropriate device using our APIs.

Here is an example of cURL request to find a device via API :

Android:

curl --location --request GET 'https://app.mobitru.com/billing/unit/<BILLING_UNIT>/automation/api/device/android?model=Pixel 6' \
--header 'Authorization: Bearer <ACCESS_KEY>'

iOS:

curl --location --request GET 'https://app.mobitru.com/billing/unit/<BILLING_UNIT>/automation/api/device/ios?version=16.2' \
--header 'Authorization: Bearer <ACCESS_KEY>'

Below you can find a sample of the response with capabilities for found devices:

Android:

[
    {
        "desiredCapabilities": {
            "platformName": "Android",
            "platformVersion": "12",
            "deviceName": "GOOGLE Pixel 6a",
            "udid": "26281JEGR04493"
        }
    },
    {
        "desiredCapabilities": {
            "platformName": "Android",
            "platformVersion": "12",
            "deviceName": "GOOGLE Pixel 6",
            "udid": "19161FDF600DJP"
        }
    },
    {
        "desiredCapabilities": {
            "platformName": "Android",
            "platformVersion": "13",
            "deviceName": "GOOGLE Pixel 6 Pro",
            "udid": "1A281FDEE007T3"
        }
    }
]


iOS:

[
    {
        "desiredCapabilities": {
            "platformName": "iOS",
            "platformVersion": "16.2",
            "deviceName": "IPHONE iPhone13,3",
            "udid": "00008101-00042D8A1190001E",
            "automationName": "XCUITest"
        }
    },
    {
        "desiredCapabilities": {
            "platformName": "iOS",
            "platformVersion": "16.2",
            "deviceName": "IPAD iPad12,1",
            "udid": "00008030-001E79460A10C02E",
            "automationName": "XCUITest"
        }
    },
    {
        "desiredCapabilities": {
            "platformName": "iOS",
            "platformVersion": "16.2",
            "deviceName": "IPHONE iPhone13,2",
            "udid": "00008101-001219601A51003A",
            "automationName": "XCUITest"
        }
    }
]

Here is an example of cURL request to take a device via API:

Android:

curl --location --request POST 'https://app.mobitru.com/billing/unit/<BILLING_UNIT>/automation/api/device/26281JEGR04493' \
--header 'Authorization: Bearer <ACCESS_KEY>'

iOS:

curl --location --request POST 'https://app.mobitru.com/billing/unit/<BILLING_UNIT>/automation/api/device/00008101-00042D8A1190001E' \
--header 'Authorization: Bearer <ACCESS_KEY>'

Below you can find a sample of the response with capabilities for the device:

Android:

{ 
    "desiredCapabilities": {
        "platformName": "Android",
        "platformVersion": "12",
        "deviceName": "GOOGLE Pixel 6a",
        "udid": "26281JEGR04493"
    }
}

iOS:

{
    "desiredCapabilities": {
        "platformName": "iOS",
        "platformVersion": "16.2",
        "deviceName": "IPHONE iPhone13,3",
        "udid": "00008101-00042D8A1190001E"
    }
}

Step 6: Install the app on the device

Install the built App on the Device before starting the first test.

Here is an example of cURL request to install the app via API (udid from step 3 and app id from step 2):

curl --location --request GET 'https://app.mobitru.com/billing/unit/<BILLING_UNIT>/automation/api/storage/install/26281JEGR04493/b7b0aae5-a839-4c15-b6ba-98edb75d07a1' \
--header 'Authorization: Bearer <ACCESS_KEY>'

As a result, you will receive 201 Created response.

Step 7: Run sample test

After installing the built app, you can run your first test using all existing information:

const wdio = require('webdriverio');
const assert = require('assert');
const find = require('appium-flutter-finder');


const osSpecificOps =
    process.env.APPIUM_OS === 'android'
        ? {
            'appium:platformName': 'Android',
            'appium:udid': '26281JEGR04493',
            'appium:appPackage': 'com.example.flutter_demo_app',
            'appium:appActivity': 'com.example.flutter_demo_app.MainActivity',
            'mobitru:appiumVersion': '2',
            'appium:keepDevice': true,
            'appium:dontStopAppOnReset': true,
        }
        : process.env.APPIUM_OS === 'ios'
            ? {
                'appium:platformName': 'iOS',
                'appium:udid': '00008101-00042D8A1190001E',
                'mobitru:appiumVersion': '2',
                'appium:bundleId': 'com.mobitru.flutterDemoApp',
                'appium:noReset': true,
                'appium:autoAcceptAlerts': true,
                'appium:keepDevice': true,
            }
            : {};

const opts = {
    port: 443,
    path: '/wd/hub',
    protocol: 'https',
    hostname: '<BILLING_UNIT>:<ACCESS_KEY>@app.mobitru.com',
    capabilities: {
        ...osSpecificOps,
        'appium:automationName': 'Flutter'
    }
};

describe('Appium fullter driver sample app', () => {
    it('perform login', async () => {
        //describe elements
        const loginInput = find.ancestor({
            of: find.byText('Login'),
            matching: find.byType('InputWidget')
        });
        const passwordInput = find.ancestor({
            of: find.byText('Password'),
            matching: find.byType('InputWidget')
        });
        const signInButton = find.byText('Sign In')

        const sortingMode = find.descendant({
            of: find.byType('SortingIcon'),
            matching: find.byType('Text')
        });

        const addToCartButton = find.byText('Add to cart')


        //start a session
        const driver = await wdio.remote(opts);

        //check driver works
        assert.strictEqual(await driver.execute('flutter:checkHealth'), 'ok');

        //wait for screen loaded
        await driver.execute('flutter:waitFor', loginInput, 10000)

        //log full elements tree
        const tree = driver.execute('flutter: getRenderTree');
        console.log(tree)

        //execute service commands
        await driver.execute('flutter:clearTimeline');
        await driver.execute('flutter:forceGC');


        //perform login
        await driver.elementSendKeys(loginInput, 'testuser@mobitru.com')
        await driver.elementSendKeys(passwordInput, 'password1')
        await driver.elementClick(signInButton)

        //wait for first product appears
        await driver.execute('flutter:waitFor', addToCartButton, 10000)

        //check sorting mode
        assert.strictEqual(await driver.getElementText(sortingMode), 'Price');

    })
})


Scroll to Top