Software Testing
Software Testing
Automating Multilingual Mobile Tests with Maestro
Automating Multilingual Mobile Tests with Maestro
Automating Multilingual Mobile Tests with Maestro
May 19, 2025
|
10
min read

In the era of AI-powered code generation, quality assurance remains the critical bottleneck in software development. For mobile applications targeting international markets, multilingual testing is non-negotiable. This guide demonstrates how to implement efficient, automated multilingual UI testing using Maestro Studio.
Why Maestro for Mobile Testing?
Maestro offers significant advantages for modern mobile testing workflows:
Simple YAML-based syntax requiring no coding expertise
Cross-platform support Android & iOS
Fast test execution
Optimized for end-to-end flows and UI assertions
The Importance of Multilingual Testing
The importance of multilingual testing are listed:
Market Penetration Applications that support native languages drive higher adoption rates
User Experience Proper localization creates a seamless, intuitive experience
Regulatory Compliance Many regions require applications to support local languages
Getting Started with Maestro
Installation
Install Maestro by following the official documentation
Install your application on an emulator or physical device:
adb install yourapp.apk
Check installation:
bash adb shell pm list packages # or adb devices
Creating Your First Test Flow
Let's create a basic test flow that launches an app and performs simple interactions:
Create a YAML file ( example_flow.yaml ):
appId: org.wikimedia.wikipedia --- - launchApp - tapOn: "Skip" - tapOn: text: "Search Wikipedia" - inputText: "User" - scrollUntilVisible: element: "User interface" direction: DOWN - tapOn: "User interface" - assertVisible: "Means by which a user interacts .*"
Run the flow:
Implementing Multilingual Testing
The key to multilingual testing is a structured approach with translation files and templated test flows.
1. Create a Translation Structure
First, organize translations in a JSON file ( translations.json ):
{ "en": { "emailLabel": "Email*", "passwordLabel": "password*", "loginButton": "Login" }, "de": { "emailLabel": "Email*", "passwordLabel": "Passwort*", "loginButton": "Login" }, "no": { "emailLabel": "E-post*", "passwordLabel": "Passord*", "loginButton": "Logg inn"
2. Create Template Test Flows
Design test flows with placeholders that will be replaced with language-specific strings:
appId: com.example.MyApp --- - launchApp - tapOn: "${emailLabel}" - inputText: "youremail@gmail.com" - hideKeyboard - tapOn: "${passwordLabel}" - inputText: "yourpassword" - hideKeyboard - tapOn: "${loginButton}" - extendedWaitUntil: visible: "home"
3. Build an Automated Test Runner
Create a Node.js script to run tests across multiple languages:
Step1 Loading translations and preparing translated content
const { exec } = require("child_process"); const fs = require("fs"); const path = require("path"); // Load translations const translations = JSON.parse(fs.readFileSync("translations.json")); // Process flow with translations function processFlowWithTranslations(flowPath, lang) { const content = fs.readFileSync(flowPath, "utf8"); const langTranslations = translations[lang] || translations["en"]; return content.replace(/\${(\w+)}/g, (_, key) => { return langTranslations[key] || `__MISSING_${key}__`; }); }
This section loads translations from a translations.json file, then dynamically replaces ${key} variables in a flow YAML file using the requested language. If a translation is missing, it displays a MISSING_key flag
Step2 run translated flow into a language
async function runFlow(flowPath, language) { console.log(`Running flow in ${language}...`); // Create translated temporary flow const translatedFlow = processFlowWithTranslations(flowPath, language); const tempPath = path.join(__dirname, `temp_flow_${language}.yaml`); fs.writeFileSync(tempPath, translatedFlow); // Execute the flow return new Promise((resolve) => { exec(`maestro test "${tempPath}"`, (error, stdout, stderr) => { fs.unlinkSync(tempPath); // Clean up if (error) { console.error(`Failed in ${language}: ${stderr}`); resolve(false); } else { console.log(`Success in ${language}`); resolve(true); } }); }); }
This function creates a temporary file containing the flow with translations, then uses the maestro test command to execute it. The file is deleted once execution is complete. It returns true or false depending on the success of the test.
Step3 run the test flow using the selected language
async function runTests() { const languages = ["en", "de", "no"]; const testFlows = ["login_flow.yaml"]; const results = {}; for (const flow of testFlows) { results[flow] = {}; for (const lang of languages) { results[flow][lang] = await runFlow(flow, lang); } } console.log("Test Results:", results); } runTests();
This section defines which languages and flow files to test. It runs each flow in each language, collects the results, and prints a summary of all test outcomes to the console.
Best Practices
1.Always Hide the Keyboard Call hideKeyboard after text input to prevent UI element obstruction
Use Unique Identifiers Prefer IDs over text when possible for more stable tests
Handle Animation Use waitForAnimationToEnd or extendedWaitUntil for dynamic UI elements
Maintain Clean Separation Keep translations, flow definitions, and test logic separate
Key Maestro Commands
Take away
Implementing multilingual testing with Maestro creates a robust framework that ensures your application works correctly across all supported languages. By automating this process, you significantly reduce testing time while increasing coverage and confidence in your application's global readiness.
The key advantages of this approach are:
Consistent testing across languages
Maintainable test suites with centralized translations
Rapid feedback on localization issues
In the era of AI-powered code generation, quality assurance remains the critical bottleneck in software development. For mobile applications targeting international markets, multilingual testing is non-negotiable. This guide demonstrates how to implement efficient, automated multilingual UI testing using Maestro Studio.
Why Maestro for Mobile Testing?
Maestro offers significant advantages for modern mobile testing workflows:
Simple YAML-based syntax requiring no coding expertise
Cross-platform support Android & iOS
Fast test execution
Optimized for end-to-end flows and UI assertions
The Importance of Multilingual Testing
The importance of multilingual testing are listed:
Market Penetration Applications that support native languages drive higher adoption rates
User Experience Proper localization creates a seamless, intuitive experience
Regulatory Compliance Many regions require applications to support local languages
Getting Started with Maestro
Installation
Install Maestro by following the official documentation
Install your application on an emulator or physical device:
adb install yourapp.apk
Check installation:
bash adb shell pm list packages # or adb devices
Creating Your First Test Flow
Let's create a basic test flow that launches an app and performs simple interactions:
Create a YAML file ( example_flow.yaml ):
appId: org.wikimedia.wikipedia --- - launchApp - tapOn: "Skip" - tapOn: text: "Search Wikipedia" - inputText: "User" - scrollUntilVisible: element: "User interface" direction: DOWN - tapOn: "User interface" - assertVisible: "Means by which a user interacts .*"
Run the flow:
Implementing Multilingual Testing
The key to multilingual testing is a structured approach with translation files and templated test flows.
1. Create a Translation Structure
First, organize translations in a JSON file ( translations.json ):
{ "en": { "emailLabel": "Email*", "passwordLabel": "password*", "loginButton": "Login" }, "de": { "emailLabel": "Email*", "passwordLabel": "Passwort*", "loginButton": "Login" }, "no": { "emailLabel": "E-post*", "passwordLabel": "Passord*", "loginButton": "Logg inn"
2. Create Template Test Flows
Design test flows with placeholders that will be replaced with language-specific strings:
appId: com.example.MyApp --- - launchApp - tapOn: "${emailLabel}" - inputText: "youremail@gmail.com" - hideKeyboard - tapOn: "${passwordLabel}" - inputText: "yourpassword" - hideKeyboard - tapOn: "${loginButton}" - extendedWaitUntil: visible: "home"
3. Build an Automated Test Runner
Create a Node.js script to run tests across multiple languages:
Step1 Loading translations and preparing translated content
const { exec } = require("child_process"); const fs = require("fs"); const path = require("path"); // Load translations const translations = JSON.parse(fs.readFileSync("translations.json")); // Process flow with translations function processFlowWithTranslations(flowPath, lang) { const content = fs.readFileSync(flowPath, "utf8"); const langTranslations = translations[lang] || translations["en"]; return content.replace(/\${(\w+)}/g, (_, key) => { return langTranslations[key] || `__MISSING_${key}__`; }); }
This section loads translations from a translations.json file, then dynamically replaces ${key} variables in a flow YAML file using the requested language. If a translation is missing, it displays a MISSING_key flag
Step2 run translated flow into a language
async function runFlow(flowPath, language) { console.log(`Running flow in ${language}...`); // Create translated temporary flow const translatedFlow = processFlowWithTranslations(flowPath, language); const tempPath = path.join(__dirname, `temp_flow_${language}.yaml`); fs.writeFileSync(tempPath, translatedFlow); // Execute the flow return new Promise((resolve) => { exec(`maestro test "${tempPath}"`, (error, stdout, stderr) => { fs.unlinkSync(tempPath); // Clean up if (error) { console.error(`Failed in ${language}: ${stderr}`); resolve(false); } else { console.log(`Success in ${language}`); resolve(true); } }); }); }
This function creates a temporary file containing the flow with translations, then uses the maestro test command to execute it. The file is deleted once execution is complete. It returns true or false depending on the success of the test.
Step3 run the test flow using the selected language
async function runTests() { const languages = ["en", "de", "no"]; const testFlows = ["login_flow.yaml"]; const results = {}; for (const flow of testFlows) { results[flow] = {}; for (const lang of languages) { results[flow][lang] = await runFlow(flow, lang); } } console.log("Test Results:", results); } runTests();
This section defines which languages and flow files to test. It runs each flow in each language, collects the results, and prints a summary of all test outcomes to the console.
Best Practices
1.Always Hide the Keyboard Call hideKeyboard after text input to prevent UI element obstruction
Use Unique Identifiers Prefer IDs over text when possible for more stable tests
Handle Animation Use waitForAnimationToEnd or extendedWaitUntil for dynamic UI elements
Maintain Clean Separation Keep translations, flow definitions, and test logic separate
Key Maestro Commands
Take away
Implementing multilingual testing with Maestro creates a robust framework that ensures your application works correctly across all supported languages. By automating this process, you significantly reduce testing time while increasing coverage and confidence in your application's global readiness.
The key advantages of this approach are:
Consistent testing across languages
Maintainable test suites with centralized translations
Rapid feedback on localization issues
In the era of AI-powered code generation, quality assurance remains the critical bottleneck in software development. For mobile applications targeting international markets, multilingual testing is non-negotiable. This guide demonstrates how to implement efficient, automated multilingual UI testing using Maestro Studio.
Why Maestro for Mobile Testing?
Maestro offers significant advantages for modern mobile testing workflows:
Simple YAML-based syntax requiring no coding expertise
Cross-platform support Android & iOS
Fast test execution
Optimized for end-to-end flows and UI assertions
The Importance of Multilingual Testing
The importance of multilingual testing are listed:
Market Penetration Applications that support native languages drive higher adoption rates
User Experience Proper localization creates a seamless, intuitive experience
Regulatory Compliance Many regions require applications to support local languages
Getting Started with Maestro
Installation
Install Maestro by following the official documentation
Install your application on an emulator or physical device:
adb install yourapp.apk
Check installation:
bash adb shell pm list packages # or adb devices
Creating Your First Test Flow
Let's create a basic test flow that launches an app and performs simple interactions:
Create a YAML file ( example_flow.yaml ):
appId: org.wikimedia.wikipedia --- - launchApp - tapOn: "Skip" - tapOn: text: "Search Wikipedia" - inputText: "User" - scrollUntilVisible: element: "User interface" direction: DOWN - tapOn: "User interface" - assertVisible: "Means by which a user interacts .*"
Run the flow:
Implementing Multilingual Testing
The key to multilingual testing is a structured approach with translation files and templated test flows.
1. Create a Translation Structure
First, organize translations in a JSON file ( translations.json ):
{ "en": { "emailLabel": "Email*", "passwordLabel": "password*", "loginButton": "Login" }, "de": { "emailLabel": "Email*", "passwordLabel": "Passwort*", "loginButton": "Login" }, "no": { "emailLabel": "E-post*", "passwordLabel": "Passord*", "loginButton": "Logg inn"
2. Create Template Test Flows
Design test flows with placeholders that will be replaced with language-specific strings:
appId: com.example.MyApp --- - launchApp - tapOn: "${emailLabel}" - inputText: "youremail@gmail.com" - hideKeyboard - tapOn: "${passwordLabel}" - inputText: "yourpassword" - hideKeyboard - tapOn: "${loginButton}" - extendedWaitUntil: visible: "home"
3. Build an Automated Test Runner
Create a Node.js script to run tests across multiple languages:
Step1 Loading translations and preparing translated content
const { exec } = require("child_process"); const fs = require("fs"); const path = require("path"); // Load translations const translations = JSON.parse(fs.readFileSync("translations.json")); // Process flow with translations function processFlowWithTranslations(flowPath, lang) { const content = fs.readFileSync(flowPath, "utf8"); const langTranslations = translations[lang] || translations["en"]; return content.replace(/\${(\w+)}/g, (_, key) => { return langTranslations[key] || `__MISSING_${key}__`; }); }
This section loads translations from a translations.json file, then dynamically replaces ${key} variables in a flow YAML file using the requested language. If a translation is missing, it displays a MISSING_key flag
Step2 run translated flow into a language
async function runFlow(flowPath, language) { console.log(`Running flow in ${language}...`); // Create translated temporary flow const translatedFlow = processFlowWithTranslations(flowPath, language); const tempPath = path.join(__dirname, `temp_flow_${language}.yaml`); fs.writeFileSync(tempPath, translatedFlow); // Execute the flow return new Promise((resolve) => { exec(`maestro test "${tempPath}"`, (error, stdout, stderr) => { fs.unlinkSync(tempPath); // Clean up if (error) { console.error(`Failed in ${language}: ${stderr}`); resolve(false); } else { console.log(`Success in ${language}`); resolve(true); } }); }); }
This function creates a temporary file containing the flow with translations, then uses the maestro test command to execute it. The file is deleted once execution is complete. It returns true or false depending on the success of the test.
Step3 run the test flow using the selected language
async function runTests() { const languages = ["en", "de", "no"]; const testFlows = ["login_flow.yaml"]; const results = {}; for (const flow of testFlows) { results[flow] = {}; for (const lang of languages) { results[flow][lang] = await runFlow(flow, lang); } } console.log("Test Results:", results); } runTests();
This section defines which languages and flow files to test. It runs each flow in each language, collects the results, and prints a summary of all test outcomes to the console.
Best Practices
1.Always Hide the Keyboard Call hideKeyboard after text input to prevent UI element obstruction
Use Unique Identifiers Prefer IDs over text when possible for more stable tests
Handle Animation Use waitForAnimationToEnd or extendedWaitUntil for dynamic UI elements
Maintain Clean Separation Keep translations, flow definitions, and test logic separate
Key Maestro Commands
Take away
Implementing multilingual testing with Maestro creates a robust framework that ensures your application works correctly across all supported languages. By automating this process, you significantly reduce testing time while increasing coverage and confidence in your application's global readiness.
The key advantages of this approach are:
Consistent testing across languages
Maintainable test suites with centralized translations
Rapid feedback on localization issues