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:
- Open
pubspec.yaml
file in IDE or any editor. - Add
flutter_test
andflutter_driver
to dev_dependencies:dev_dependencies: ... flutter_test: sdk: flutter flutter_driver: sdk: flutter
- Run
flutter pub get
command to install dependencies added in the previous step. - Open
lib/main.dart
and add the following import:import 'package:flutter_driver/driver_extension.dart';
- 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:
- Open your Flutter project in IDE or in the Terminal.
- In case of Terminal, please make sure that the flutter command line tool is available (run ‘
flutter doctor
‘ command to verify) - After that, run one of the following commands:
Android: flutter build apk --debug iOS: flutter build ipa --profile
- 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
- Node JS
- JS test framework
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');
})
})