Rabiul Alam brings a blend of manual and automation expertise to the world of software testing. With a deep understanding of the entire testing life-cycle for dynamic web and mobile applications. His experience spans popular testing frameworks like Selenium, TestNG, and Appium. Additionally, he possesses hands-on knowledge of DevOps tools like Docker, ELK stack, and CI/CD pipelines.
Utilizing Appium for testing Flutter applications is a common practice in the industry. While there may be limited beginner tutorials for Appium, but the official documentation on the Appium website provides valuable guidance. Before delving into Appium, it is essential to have a grasp of how Flutter applications function.
Prior to configuring Appium, it is necessary to have Node.js installed on your system. Ensure that you install the latest version of Node.js; for instance, I am currently using Node.js version 18.16.1 and npm version 9.5.1. After installing Node.js, proceed to install Appium by executing the following command.
npm install -g [email protected]
Consider installing the most recent version available. Subsequently, it is necessary to install Appium drivers such as UIAutomator2, Flutter, XCUItest, etc. Execute the provided commands to install these drivers.
appium driver install --source=npm appium-uiautomator2-driver
appium driver install
--source=npm
appium-uiautomator2-driver
appium driver install --source=npm appium-flutter-driver
appium driver install
--source=npm
appium-flutter-driver
Feel free to customize the versions according to your preference, although it is advisable to transition to the latest releases. Once Node, Appium, and the relevant Appium drivers are installed, ensure that you can view the versions of each component. To display the list of Appium drivers, use the following command.
appium driver list --installed
appium driver list
--installed
Assuming you already have a Flutter project, within this project, add the following driver configurations to your pubspec.yaml file.
flutter_driver:
sdk: flutter
Following the addition of the drivers in the pubspec.yaml file, execute the command “flutter pub get” to obtain the added driver packages. In your main.dart file, initialize the Flutter driver by incorporating the following method.
“enableFlutterDriverExtension();”
Within your project, include the specified finder inside the respective widgets as required. It’s important to note that not all widgets support every type of finder. Prior to implementing a finder into a widget, ensure that the widget supports the specific finder you are using.
Some common methods for identifying and interacting with Flutter widgets using Finders in Appium tests. Remember to refer to the official documentation for Flutter and appium-flutter-finder for the latest information and comprehensive usage details.
TextField(key: const ValueKey("emailField"), decoration: InputDecoration(labelText:"Email",),)
TextField(key:
const ValueKey("emailField"),
decoration:
InputDecoration(labelText:"Email",)
,)
WebElement emailField = find.byValueKey("emailField");
WebElement emailField
= find.byValueKey("emailField");
BottomNavigationBarItem(
icon: Icon(Icons.shop),
label: 'shop',
tooltip: 'shop',
),
WebElement barItem = find.byToolTip("shop");
WebElement barItem
= find.byToolTip("shop");
class UniqueButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: () {},
child: Text('Unique Button'),
);
}
}
class UniqueButton extends
StatelessWidget {
@override
Widget build
(BuildContext context) {
return ElevatedButton(
onPressed: () {},
child: Text('Unique Button'),
);
}
}
WebElement button = find.byType('ElevatedButton').withText('Unique Button');
WebElement button
= find.byType('ElevatedButton')
.withText('Unique Button');
Text('Login', key: Key('loginText'))
WebElement login = find.byText('Login');
WebElement login
= find.byText('Login');
Icon(Icons.info, semanticsLabel: 'Info',)
Icon(Icons.info, semanticsLabel:
'Info'
,)
WebElement info = find.bySemanticsLabel("Info");
WebElement info
= find.bySemanticsLabel("Info");
As an example, I’ve included the following code that I utilized in my Flutter project. This code facilitates Flutter finder in identifying two input fields – one for the email and another for the password, as well as the button. Ensure that the widgets support these finders for accurate identification.
class Login extends StatefulWidget {
const Login({Key? key}) : super(key: key);
@override
State<Login> createState() => _LoginState();
}
class _LoginState extends State<Login> {
// Controllers for text fields
final TextEditingController emailController = TextEditingController();
final TextEditingController passwordController = TextEditingController();
// Flags for form validation and button state
bool isButtonEnabled = false;
@override
Widget build(BuildContext context) {
// Using SingleChildScrollView to avoid overflow when keyboard is visible
return Scaffold(
body: SingleChildScrollView(
child: Container(
height: MediaQuery.of(context).size.height,
padding: const EdgeInsets.all(20),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
_buildEmailField(),
const SizedBox(height: 20),
_buildPasswordField(),
const SizedBox(height: 20),
_buildLoginButton(),
],
),
),
),
);
}
// Email field with validation
Widget _buildEmailField() {
return TextField(
key: const ValueKey("emailField"), // Finder: byValueKey('emailField')
controller: emailController,
onChanged: (value) => _validateForm(),
decoration: InputDecoration(
labelText: "Email",
hintText: "Enter your email",
border: const OutlineInputBorder(),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
width: 1,
color: _isValidEmail(emailController.text) ? Colors.green : Colors.red,
),
),
),
);
}
// Password field with validation
Widget _buildPasswordField() {
return TextField(
key: const ValueKey(
"passwordField"), // Finder: byValueKey('passwordField')
controller: passwordController,
onChanged: (value) => _validateForm(),
obscureText: true,
decoration: InputDecoration(
labelText: "Password",
hintText: "Enter your password",
border: const OutlineInputBorder(),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
width: 1,
color: _isValidPassword(passwordController.text) ? Colors.green : Colors.red,
),
),
),
);
}
// Login button that is enabled/disabled based on form validation
Widget _buildLoginButton() {
return SizedBox(
width: double.infinity,
child: ElevatedButton(
key: const ValueKey("loginButton"), // Finder: byValueKey('loginButton')
onPressed: isButtonEnabled ? _login : null,
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.all(10),
backgroundColor: isButtonEnabled ? Colors.blue : Colors.grey,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
),
child: const Text(
"Login",
style: TextStyle(fontSize: 20, color: Colors.white),
),
),
);
}
// Form validation logic
void _validateForm() {
final isEmailValid = _isValidEmail(emailController.text);
final isPasswordValid = _isValidPassword(passwordController.text);
setState(() {
isButtonEnabled = isEmailValid && isPasswordValid;
});
}
// Email validation using regular expression
bool _isValidEmail(String email) {
if (email.isEmpty) return false;
final emailRegex = RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$');
return emailRegex.hasMatch(email);
}
// Simple password validation
bool _isValidPassword(String password) {
return password.isNotEmpty && password.length >= 8;
}
// Mock login function for demonstration
void _login() {
// Check if email and password match the expected credentials
if (emailController.text == "[email protected]" &&
passwordController.text == "12345678") {
// Navigate to the LandingPage if the credentials match
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) => LandingPage()),
);
}
else {
// Show a SnackBar if the email and password do not match
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Email or Password is incorrect'),
duration: Duration(seconds: 1),
action: SnackBarAction(
label: 'Dismiss',
onPressed: () {
// This will dismiss the SnackBar before its duration expires
ScaffoldMessenger.of(context).hideCurrentSnackBar();
},
),
),
);
}
}
}
After completing the setup and implementation of Appium with your Flutter project, you can generate a debug build APK (Android Package) by using the following command:
flutter build apk --debug
Ensure that you use either the debug or profile build for testing with Appium and avoid using the release build. This is important for compatibility and debugging purposes.
With these configurations and considerations, your Appium setup for testing your Flutter application is complete. If you encounter any issues or need further assistance, refer to the relevant documentation or seek support from the Flutter and Appium communities.
Certainly, you can use various programming languages like Python, Java, JavaScript, etc. In my case, I’ve choosed Java. Here are the general steps to set up a Maven project for Appium testing.
Firstly, ensure you have Java installed (preferably version 11 or later) and the JAVA_HOME and ANDROID_HOME environment variables are set. Then, you can create a Maven project using an IDE like VSCode or Eclipse. Once the project is created, add the necessary dependencies in the pom.xml file. Here is a dependency list for configuring your pom.xml file:
Exactly, once you’ve added the dependencies in the pom.xml file, you should run the Maven command mvn install or mvn package install to download and install all the specified dependencies automatically. Make sure, maven is installed on your system.
Afterwards, you can check if Appium is working by running the command appium in the terminal. This starts the Appium server, including the installed drivers. By default, the Appium server listens at “http://127.0.0.1:4723/“.
Ensure that your emulator or real device is powered on to run the Flutter app. Through the Appium client, the communication with the Appium server is established, enabling the execution of operations on the connected device. This connection is crucial for the testing process.
DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.setCapability("deviceName", "Your_Device_Name"); // Replace with your device name
capabilities.setCapability("platformName", "Android");
capabilities.setCapability("noReset", true);
capabilities.setCapability("app", "/path/to/your/app-debug.apk"); // Replace with your APK path
capabilities.setCapability("automationName", "Flutter");
driver = new AppiumDriver(new URL("http://0.0.0.0:4723"), capabilities);
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(5));
DesiredCapabilities capabilities
= new DesiredCapabilities();
capabilities
.setCapability("deviceName",
"Your_Device_Name");
// Replace with your device name
capabilities
.setCapability("platformName",
"Android");
capabilities
.setCapability("noReset",
true);
capabilities
.setCapability("app",
"/path/to/your/app-debug.apk");
// Replace with your APK path
capabilities
.setCapability("automationName",
"Flutter");
driver
= new AppiumDriver
(new URL("http://0.0.0.0:4723"),
capabilities);
driver.manage().timeouts()
.implicitlyWait
(Duration.ofSeconds(5));
Here,
Bash
adb devices
This will list connected devices and their names. Use the displayed device name in the deviceName capability.
This refers to the path of the Android application (.apk) you want to test. In the provided code, it’s set to /home/user/ StudioProjects/ demo_flutter/build/ app/outputs/flutter-apk/ app-debug.apk. Replace this path with the actual location of your app’s .apk file on your computer.
automationName: This is set to “Flutter” as the test framework is Flutter. You wouldn’t need to modify this unless you’re using a different framework.
You should substitute placeholders such as the device name and APK path with your actual device ID and the specific location of the APK file. It’s advisable to set up your project directory build path to dynamically incorporate the latest APK without the need for manual copying. After executing the code, the app should run successfully on your device. You can find an example Maven project for reference here.
When automating interactions with your Flutter app, it’s crucial to use identifiers. The identifier you use should correspond to the Flutter finder locator utilized in your app. Consider the following code for reference:
String safeEmail = "[email protected]";
String password = "12345678";
WebElement safeEmail = find.byValueKey("emailField");
phoneNumberField.sendKeys(safeEmail);
WebElement passwordField = find.byValueKey("password");
passwordField.sendKeys(password);
WebElement loginButton = find.byValueKey("loginButton");
loginButton.click();
String safeEmail
="[email protected]";
String password
="12345678";
WebElement safeEmail=
find.byValueKey("emailField");
phoneNumberField
.sendKeys(safeEmail);
WebElement passwordField=
find.byValueKey("password");
passwordField
.sendKeys(password);
WebElement loginButton=
find.byValueKey("loginButton");
loginButton.click();
In this example, identifiers are used to locate and interact with Flutter app elements such as the phone number field, password field, and login button. Adjust the identifiers based on the locator strategy used in your Flutter app.
when using the ValueKey identifier inside your Flutter app, you should use the byValueKey a strategy for Appium. This allows you to locate the desired field and perform actions such as tapping on it if it’s tappable or sending a specific value if it’s an input field. This code demonstrates using byValueKey to locate and interact with Flutter app elements based on the ValueKey identifiers declared in your Flutter app. Adjust the values as per your Flutter app’s implementation.
Appium offers a powerful solution for automating UI tests in your Flutter applications. By leveraging Appium and the appium-flutter-finder library, you can establish a robust testing framework to ensure the quality and functionality of your Flutter app. This guide provided a comprehensive overview of the setup process, including Appium installation, Flutter project configuration, Appium driver installation, Maven project setup for Java testing, and practical examples for interacting with Flutter widgets using Appium. Remember to refer to the official documentation for the latest information and explore advanced features as you delve deeper into Appium testing for your Flutter projects. By following these steps and best practices, you can unlock the potential of Appium and streamline your Flutter app testing process.
Tags : technology