My Demo App Flutter is a mobile application developed using Flutter based on Flutter Counter example application. Modified by the Sauce Labs team, this app is designed to demonstrate the robust capabilities of Sauce Labs' mobile devices cloud, with a particular focus on our integration with the Appium Flutter Integration Driver.
You can find additional code examples in our training repository
To build and use this demo app, ensure you have the following dependencies installed on your local machine:
- Flutter SDK
- For Android applications:
- For iOS applications:
To verify that all the necessary requirements are met, run the following command. This will check your system and provide a report on what is installed and what needs to be installed:
flutter doctor
First, install all necessary dependencies:
flutter packages get
Check for any outdated packages:
flutter pub outdated
Upgrade outdated packages to their latest major versions:
flutter pub upgrade --major-versions
At Sauce Labs, we support the appium flutter integration driver. You can follow the next steps to build your app for appium testing.
Navigate to the android directory and build the app:
cd android
./gradlew app:assembleDebug -Ptarget=`pwd`/../integration_test/appium_test.dart
For Simulator - Debug mode
flutter build ios integration_test/appium_test.dart --simulator
For Real Device - Release mode
flutter build ipa --release integration_test/appium_test.dart
In the following part we have a step-by-step guide to prepare your application for flutter integration testsing using Android's and Apple's
native test frameworks, Espresso
and XCTest
.
-
Open your Flutter project in your favorite IDE.
-
In your Flutter app's
pubspec.yaml
, add the following dependencies:dev_dependencies: integration_test: sdk: flutter flutter_test: sdk: flutter
-
Create an instrumentation test file in your application’s
android/app/src/androidTest/java/com/example/myapp
directory. Replacecom, example,
andmyApp
with the values from your app’s package name.Then, name this test file as
MainActivityTest.java
or another name of your choice.package com.example.myApp; import androidx.test.rule.ActivityTestRule; import dev.flutter.plugins.integration_test.FlutterTestRunner; import org.junit.Rule; import org.junit.runner.RunWith; import com.example.myApp.MainActivity; @RunWith(FlutterTestRunner.class) public class MainActivityTest { @Rule public ActivityTestRule<MainActivity> rule = new ActivityTestRule<>(MainActivity.class, true, false); }
-
Update your application’s
myapp/android/app/build.gradle
file to ensure it uses androidx’s version ofAndroidJUnitRunner
and includes androidx libraries as a dependency.android { ... defaultConfig { ... testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } } dependencies { testImplementation 'junit:junit:4.12' androidTestImplementation 'androidx.test:runner:1.2.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' }
-
Create a directory called
integration_test
in the root of your Flutter project. -
Create a file called
flutter_integration_test.dart
in theintegration_test
directory. -
Update your testing dart file
flutter_integration_test.dart
to include the tearDownAll, The purpose for this is to make sure we close the connection to the device after the tests have completed.import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; // Ensure you have this import // Add as app because we want to make sure the app loaded correctly on the device by calling the main function in the main dart file. import 'package:my_demo/main.dart' as app; void main() { // Ensure IntegrationTestWidgetsFlutterBinding is initialized final binding = IntegrationTestWidgetsFlutterBinding.ensureInitialized() as IntegrationTestWidgetsFlutterBinding; group('E2E Test With Flutter', (){ tearDownAll(() async { // Signal that the test is complete binding.reportData = <String, dynamic>{ 'completed': true, }; }); testWidgets("First testing scenario increment 5 decrement 3", (tester) async { app.main(); await tester.pumpAndSettle(); // wait for app to be ready. // ... }); }); }
-
Use the following
Gradle
commands to build an instrumentation test.apk
file(test suite) using theMainActivityTest.java
created in theandroidTest
directory as mentioned in step 3.# Go to the android folder which contains the "gradlew" script used for building Android apps from the terminal pushd android # Build an Android test APK (uses the MainActivityTest.java file created in step 1) ./gradlew app:assembleAndroidTest # Build a debug APK by passing the integration test file ./gradlew app:assembleDebug -Ptarget="..../integration_test/flutter_integration_test.dart" # Go back to the root of the project popd
-
Configure
saucectl
to run the test.- Create a folder
.sacue
in your project root directory. - Inside this folder create a
flutter_integration_test_android.yaml
with the following content:
apiVersion: v1alpha kind: espresso sauce: concurrency: 1 espresso: app: ...../flutter/my-demo-app-flutter/build/app/outputs/flutter-apk/app-debug.apk testApp: ....../flutter/my-demo-app-flutter/build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk suites: - name: "Sauce Labs Espresso with flutter integration tests" testOptions: class: - com.example.my_demo_app_flutter.MainActivityTest devices: - name: "Google Pixel.*" artifacts: download: when: always match: - junit.xml directory: ./
- Run the following commands to start the test on Sauce Labs
saucectl configure -u USERNAME -a ACCESS_KEY saucectl run -c sauceconnect/flutter_integration_test.yaml
- Check the status of you test on
app.saucelabs.com
12:24:52 INF Running Espresso in Sauce Labs (. #. #. .#################### #####////////*******/###### .##///////*****************###/ ,###////*********************### ####//***********************#### ###/************************### ######********************###. ## (######################## ## ## ,######(#* ##* (## /############* ##### (########( #########( ### .#######, */ ############ ,########## %#### , ########* *### .#######/ ## / ######## ### .###########//########### ###### ######################## (#( *#( #######. (####### ##, /######## ######## *######## ######## _____ _ _ _____ ______ _____ _ ____ _ _ _____ / ____| /\ | | | |/ ____| ____| / ____| | / __ \| | | | __ \ | (___ / \ | | | | | | |__ | | | | | | | | | | | | | | \___ \ / /\ \| | | | | | __| | | | | | | | | | | | | | | ____) / ____ \ |__| | |____| |____ | |____| |___| |__| | |__| | |__| | |_____/_/ \_\____/ \_____|______| \_____|______\____/ \____/|_____/ 12:24:52 INF Checking if ...../my-demo-app-flutter/build/app/outputs/flutter-apk/app-debug.apk has already been uploaded previously 12:24:52 INF Checksum: 1df0b6684973536fef4ae653d89661d6c48d6f699091511515b69735d6a80fbd 12:27:26 INF Application uploaded. durationMs=153173 storageId=6849a64a-3c51-4423-87f2-b3660c972a36 12:27:26 INF Checking if ...../build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk has already been uploaded previously 12:27:26 INF Checksum: b5b15cb741b78fe7a5df171406c17ec9ea8fd6ac52623abf7a8df519270e281d 12:27:26 INF Skipping upload, using storage:635a7a46-c1fc-4c43-9a05-60e09a2163b8 12:27:26 INF Launching workers. concurrency=1 12:27:26 INF Starting suite. region=us-west-1 suite="Sauce Labs Espresso with flutter integration tests" 12:27:27 INF Suite started. deviceId= deviceName="Google Pixel.*" platform=Android platformVersion= private=false suite="Sauce Labs Espresso with flutter integration tests" url=https://app.saucelabs.com/tests/4b52d0880d5d41579d669a66fdca2da0 12:27:36 INF Suites in progress: 1 12:27:46 INF Suites in progress: 1 12:27:56 INF Suites in progress: 1 12:28:06 INF Suites in progress: 1 12:28:16 INF Suites in progress: 1 12:28:26 INF Suites in progress: 1 12:28:27 INF Suite finished. passed=true suite="Sauce Labs Espresso with flutter integration tests" url=https://app.saucelabs.com/tests/4b52d0880d5d41579d669a66fdca2da0 Results: Name Duration Status Platform Device Attempts ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── ✔ Sauce Labs Espresso with flutter integration tests 1m0s passed Android Google Pixel.* 1 ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── ✔ All tests have passed 1m1s Build Link: https://app.saucelabs.com/builds/rdc/159d98f0223246e59dd172bad78573cc
- Create a folder
- Launch the Xcode Application.
- On Xcode, open the
ios/Runner.xcworkspace
in your flutter project, inside the ios folder. - Create a test target if you do not already have one via
File > New > Target...
and selectUnit Testing Bundle
. - Change the
Product Name
toRunnerTests
. - Make sure
Target to be Tested
is set toRunner
and language is set toObjective-C
. - Make sure that the
iOS Deployment Target
ofRunnerTests
within theBuild Settings
section is the same asRunner
. - In the new target, add a test file called
RunnerTests.m
(or any name of your choice) and replace the file content with the following :@import XCTest; @import integration_test; INTEGRATION_TEST_IOS_RUNNER(RunnerTests)
- Select your
Team
for both Targets in theSigning & Capabilities
section. - Now you can open the Flutter project in your favorite IDE.
- Update the
ios/Podfile
file by embedding in the existing Runner target :
target 'Runner' do
# Do not change existing lines.
...
target 'RunnerTests' do
inherit! :search_paths
end
end
- Create a directory called
integration_test
in the root of your Flutter project. - Create a file called
flutter_integration_test.dart
in theintegration_test
directory. - Update your testing dart file
flutter_integration_test.dart
to include the tearDownAll, The purpose for this is to make sure we close the connection to the device after the tests have completed.import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; // Ensure you have this import // Add as app because we want to make sure the app loaded correctly on the device by calling the main function in the main dart file. import 'package:my_demo/main.dart' as app; void main() { // Ensure IntegrationTestWidgetsFlutterBinding is initialized final binding = IntegrationTestWidgetsFlutterBinding.ensureInitialized() as IntegrationTestWidgetsFlutterBinding; group('E2E Test With Flutter', (){ tearDownAll(() async { // Signal that the test is complete binding.reportData = <String, dynamic>{ 'completed': true, }; }); testWidgets("First testing scenario increment 5 decrement 3", (tester) async { app.main(); await tester.pumpAndSettle(); // wait for app to be ready. // ... }); }); }
- To build
integration_test/flutter_integration_test.dart
from the command line, run.flutter build ios --config-only integration_test/flutter_integration_test.dart
- Execute the following bash script at the root of your Flutter app, this bash script will build the flutter app and generate
the
xctestrun
file which contains all the necessary configs to successfully trigger the integration testoutput="../build/ios_integration" product="build/ios_integration/Build/Products" flutter clean # Pass --simulator if building for the simulator. flutter build ios integration_test/flutter_integration_test.dart --release # move to the ios folder pushd ios # run the xcodebuild command to build the app for testing xcodebuild build-for-testing \ -workspace Runner.xcworkspace \ -scheme Runner \ -xcconfig Flutter/Release.xcconfig \ -configuration Release \ -derivedDataPath \ $output -sdk iphoneos # go back the flutter application root folder popd # Compile the app into ipa file: # Open the product folder to get the xctestrun file and the application folder pushd $product mkdir Payload cp -r Release-iphoneos/Runner.app Payload zip -r Runner.ipa Payload popd
- Configure
saucectl
to run the test.
- Create a folder
.sauce
in your project root directory. - Inside this folder create a
flutter_integration_test_ios.yaml
with the following content:apiVersion: v1alpha kind: xctest sauce: concurrency: 1 xctest: app: ...../flutter/my-demo-app-flutter/build/ios_integration/Build/Products/Runner.ipa xcTestRunFile: ....../flutter/my-demo-app-flutter/build/ios_integration/Build/Products/Runner_iphoneos18.1-arm64.xctestrun suites: - name: "Sauce Labs XCTest with flutter integration tests" devices: - name: ".*" artifacts: download: when: always match: - junit.xml directory: ./
- Run the following commands to start the test on Sauce Labs
saucectl configure -u USERNAME -a ACCESS_KEY saucectl run -c .sauce/flutter_integration_test_ios.yaml
- Check the status of you test on
app.saucelabs.com