You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Appium TestNG多语言环境下元素定位通用方案咨询

嘿,这个问题我之前帮不少做Appium自动化的朋友解决过——用TestNG在真机上跑自动化时,遇到元素text随语言变化导致定位失效,确实挺闹心的。不过有几个成熟的方案能帮你实现一套代码适配多语言,我给你详细拆解下:

方案一:使用本地化字符串文件(最推荐的可扩展方案)

核心思路是把不同语言的文本抽离到单独的配置文件里,测试代码根据当前设备的语言环境自动加载对应文件里的文本,再用这个文本去定位元素。

步骤1:创建多语言配置文件

在你的TestNG项目资源目录下,创建对应语言的properties文件:

  • strings_en.properties(英文):
    login_button=Log In
    welcome_message=Welcome back!
    
  • strings_zh.properties(中文):
    login_button=登录
    welcome_message=欢迎回来!
    
  • 其他语言同理,比如strings_es.properties(西班牙语)等。

步骤2:编写工具类读取对应文件

写一个工具类,根据当前设备的语言自动加载对应的properties文件:

import java.io.InputStream;
import java.util.Properties;

public class LocalizationUtils {
    private static Properties props;

    public static void loadLocalization(String languageCode) {
        props = new Properties();
        try {
            InputStream is = LocalizationUtils.class.getClassLoader()
                    .getResourceAsStream("strings_" + languageCode + ".properties");
            props.load(is);
        } catch (Exception e) {
            // 如果找不到对应语言文件,默认加载英文
            try {
                InputStream is = LocalizationUtils.class.getClassLoader()
                        .getResourceAsStream("strings_en.properties");
                props.load(is);
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
    }

    public static String getString(String key) {
        return props.getProperty(key);
    }
}

步骤3:在测试代码中动态获取文本定位元素

先获取当前设备的语言,加载对应配置,再用配置里的文本定位:

import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import io.appium.java_client.android.AndroidDriver;
import org.openqa.selenium.By;

public class MultiLangTest {
    private AndroidDriver driver;

    @BeforeMethod
    public void setUp() {
        // 初始化driver的代码省略
        // 获取当前设备语言,比如"zh"或"en"
        String currentLang = driver.getDeviceLanguage();
        // 加载对应语言配置
        LocalizationUtils.loadLocalization(currentLang);
    }

    @Test
    public void testLogin() {
        // 用动态获取的文本定位登录按钮
        driver.findElement(By.xpath("//*[@text='" + LocalizationUtils.getString("login_button") + "']")).click();
    }
}
方案二:避免使用text属性,改用稳定的元素属性

很多APP在开发时会给元素设置不随语言变化的属性,比如resource-idcontent-desc(也就是 accessibility ID),这些属性一般是固定的,完全不受语言影响。

常用的替代定位方式:

  • resource-id定位:这是最稳定的,只要开发不随便改ID,几乎不会出问题
    driver.findElement(By.id("com.your.app.package:id/login_button")).click();
    
  • content-desc/accessibility ID定位:如果APP做了无障碍适配,会给元素设置这个属性,同样不随语言变化
    // Android用content-desc
    driver.findElement(By.name("login_button")).click();
    // iOS用accessibility ID
    driver.findElement(By.accessibilityId("login_button")).click();
    
  • XPath结合其他属性:如果没有resource-id,可以用class+其他属性组合,比如
    driver.findElement(By.xpath("//android.widget.Button[@content-desc='login_action']")).click();
    

这个方案的好处是完全不用处理语言逻辑,直接换定位方式就行,省心又高效,优先推荐用这个(前提是APP提供了这些稳定属性)。

方案三:动态映射语言与文本(快速临时方案)

如果以上两种方案都没法用,比如APP没有稳定属性,又不想搞配置文件,可以用一个Map来存储不同语言对应的文本,然后根据当前设备语言动态取值:

import java.util.HashMap;
import java.util.Map;

public class LangTextMapper {
    private static Map<String, Map<String, String>> langMap = new HashMap<>();

    static {
        // 初始化英文文本
        Map<String, String> enMap = new HashMap<>();
        enMap.put("login_button", "Log In");
        enMap.put("welcome_message", "Welcome back!");
        langMap.put("en", enMap);

        // 初始化中文文本
        Map<String, String> zhMap = new HashMap<>();
        zhMap.put("login_button", "登录");
        zhMap.put("welcome_message", "欢迎回来!");
        langMap.put("zh", zhMap);
    }

    public static String getText(String langCode, String key) {
        return langMap.getOrDefault(langCode, langMap.get("en")).get(key);
    }
}

然后在测试代码里用:

import org.testng.Assert;
import org.testng.annotations.Test;
import io.appium.java_client.android.AndroidDriver;
import org.openqa.selenium.By;

public class QuickLangTest {
    private AndroidDriver driver;

    @Test
    public void testWelcome() {
        String currentLang = driver.getDeviceLanguage();
        String welcomeText = LangTextMapper.getText(currentLang, "welcome_message");
        Assert.assertEquals(driver.findElement(By.xpath("//*[@text='" + welcomeText + "']")).getText(), welcomeText);
    }
}
额外小技巧:测试前自动切换设备语言

如果需要在不同语言环境下跑测试,可以用Appium的API或者adb命令提前切换语言:

// Android用adb命令切换到中文
driver.executeScript("mobile: shell", ImmutableMap.of("command", "am set-config -u 0 -l zh_CN"));
// 切换到英文
driver.executeScript("mobile: shell", ImmutableMap.of("command", "am set-config -u 0 -l en_US"));

总结一下:优先用方案二(稳定属性定位),其次是方案一(本地化文件,扩展性强),方案三适合快速临时解决问题。这样就能一套代码适配所有语言环境啦!

内容的提问来源于stack exchange,提问作者Ggaran

火山引擎 最新活动