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



Copyrights © 2025 by BIT SOLUTIONS.

Copyrights © 2025 by BIT SOLUTIONS.

Copyrights © 2025 by BIT SOLUTIONS.