如何在Java的Selenium中执行ADB命令并在AWS Device Farm运行JUnit测试
在Java Appium中执行ADB命令、部署到AWS Device Farm并获取性能数据
我来帮你一步步解决这几个问题——在Java Appium测试里执行ADB命令、部署到AWS Device Farm,还有获取应用的内存和CPU使用率,结合你的现有代码来调整:
一、在测试中执行ADB命令(推荐用Appium原生方式)
在Appium环境里,直接调用系统Runtime.exec()执行ADB命令在本地没问题,但到了AWS DeviceFarm这种云端环境会有依赖问题。更稳妥的是用Appium提供的mobile: shell扩展命令,它能直接在设备端执行shell命令(相当于adb shell),不需要依赖本地ADB。
核心代码示例
你可以封装两个工具方法,用来执行ADB命令获取性能数据:
// 获取CPU使用率(过滤目标应用包名) private String getCpuUsage(String packageName) { try { Map<String, Object> params = new HashMap<>(); params.put("command", String.format("top -n 1 | grep %s", packageName)); return (String) driver.executeScript("mobile: shell", params); } catch (Exception e) { e.printStackTrace(); return "获取CPU数据失败"; } } // 获取内存使用率(提取Total PSS指标,反映实际占用内存) private String getMemoryUsage(String packageName) { try { Map<String, Object> params = new HashMap<>(); params.put("command", String.format("dumpsys meminfo %s", packageName)); String memInfo = (String) driver.executeScript("mobile: shell", params); // 正则提取关键数据 Pattern pattern = Pattern.compile("Total PSS:\\s+(\\d+)"); Matcher matcher = pattern.matcher(memInfo); if (matcher.find()) { return String.format("Total PSS: %s KB", matcher.group(1)); } return memInfo; } catch (Exception e) { e.printStackTrace(); return "获取内存数据失败"; } }
二、获取目标应用的内存和CPU使用率
把上面的工具方法整合到你的测试流程里,比如在每次操作前后打印性能数据,方便对比:
在你的test()方法循环中,加入这些代码:
String timestamp = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss:SSS").format(new Date()); // 操作前获取性能数据 System.out.println(timestamp + " 操作前CPU使用率: " + getCpuUsage("com.androidappname.app")); System.out.println(timestamp + " 操作前内存使用率: " + getMemoryUsage("com.androidappname.app")); // 这里放你的原有操作代码(比如滑动、点击等) // 操作后再次获取性能数据 timestamp = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss:SSS").format(new Date()); System.out.println(timestamp + " 操作后CPU使用率: " + getCpuUsage("com.androidappname.app")); System.out.println(timestamp + " 操作后内存使用率: " + getMemoryUsage("com.androidappname.app"));
三、将JUnit测试部署到AWS Device Farm
AWS Device Farm支持Appium Java JUnit测试,需要做几个关键调整:
1. 调整Appium Driver初始化代码
Device Farm会自动提供Appium服务,不需要硬编码本地URL,改成兼容本地和云端的写法:
@Before public void setUp() throws Exception { System.out.println("capabilities creation start........."); DesiredCapabilities capabilities = new DesiredCapabilities(); // 移除硬编码的deviceName和version,Device Farm会自动匹配选中的设备 capabilities.setCapability("platformName", "Android"); capabilities.setCapability("appPackage","com.androidappname.app"); capabilities.setCapability("appActivity","com.androidappname.app.views.activities.SplashScreenActivity"); capabilities.setCapability("newCommandTimeout", "450000"); capabilities.setCapability("gpsEnabled",true); System.out.println("android driver loading start........."); // 兼容本地和Device Farm的Appium URL String appiumUrl = System.getenv("APPIUM_URL"); if (appiumUrl == null) { appiumUrl = "http://127.0.0.1:4723/wd/hub"; } driver = new AndroidDriver(new URL(appiumUrl), capabilities); driver.manage().timeouts().implicitlyWait(15, TimeUnit.SECONDS); }
2. 打包测试代码为Fat Jar
Device Farm需要包含所有依赖的可执行JAR,用Maven的shade插件打包,在pom.xml中添加配置:
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <version>3.2.4</version> <executions> <execution> <phase>package</phase> <goals> <goal>shade</goal> </goals> <configuration> <transformers> <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> <!-- 如果不需要主类可以去掉这行 --> <mainClass>macdjtest</mainClass> </transformer> </transformers> </configuration> </execution> </executions> </plugin> </plugins> </build>
执行mvn package就能得到包含所有依赖的Fat Jar。
3. 在AWS Device Farm中部署测试
- 登录AWS控制台,进入Device Farm服务,创建新项目。
- 选择Android平台,上传你的应用APK。
- 选择Appium Java JUnit作为测试类型,上传你打包好的测试Fat Jar。
- 选择测试设备池(公共设备池或私有设备),启动测试。
- 测试完成后,在控制台查看测试结果、日志和性能数据。
四、整合后的完整测试代码
import io.appium.java_client.android.AndroidDriver; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.openqa.selenium.By; import org.openqa.selenium.Dimension; import org.openqa.selenium.WebDriverWait; import org.openqa.selenium.remote.DesiredCapabilities; import org.openqa.selenium.support.ui.ExpectedConditions; import java.net.URL; import java.text.SimpleDateFormat; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeUnit; import java.util.regex.Matcher; import java.util.regex.Pattern; public class macdjtest { static AndroidDriver driver; Dimension size; int count=0; private static final String PACKAGE_NAME = "com.androidappname.app"; @Before public void setUp() throws Exception { System.out.println("capabilities creation start........."); DesiredCapabilities capabilities = new DesiredCapabilities(); capabilities.setCapability("platformName", "Android"); capabilities.setCapability("appPackage", PACKAGE_NAME); capabilities.setCapability("appActivity","com.androidappname.app.views.activities.SplashScreenActivity"); capabilities.setCapability("newCommandTimeout", "450000"); capabilities.setCapability("gpsEnabled",true); System.out.println("android driver loading start........."); // 兼容本地和Device Farm的Appium URL String appiumUrl = System.getenv("APPIUM_URL"); if (appiumUrl == null) { appiumUrl = "http://127.0.0.1:4723/wd/hub"; } driver = new AndroidDriver(new URL(appiumUrl), capabilities); driver.manage().timeouts().implicitlyWait(15, TimeUnit.SECONDS); } @After public void tearDown() throws Exception { if (driver != null) { driver.quit(); } } // 获取CPU使用率 private String getCpuUsage() { try { Map<String, Object> params = new HashMap<>(); params.put("command", String.format("top -n 1 | grep %s", PACKAGE_NAME)); return (String) driver.executeScript("mobile: shell", params); } catch (Exception e) { e.printStackTrace(); return "获取CPU数据失败"; } } // 获取内存使用率(Total PSS) private String getMemoryUsage() { try { Map<String, Object> params = new HashMap<>(); params.put("command", String.format("dumpsys meminfo %s", PACKAGE_NAME)); String memInfo = (String) driver.executeScript("mobile: shell", params); Pattern pattern = Pattern.compile("Total PSS:\\s+(\\d+)"); Matcher matcher = pattern.matcher(memInfo); if (matcher.find()) { return String.format("Total PSS: %s KB", matcher.group(1)); } return "内存详情:\n" + memInfo; } catch (Exception e) { e.printStackTrace(); return "获取内存数据失败"; } } @Test public void test() throws Exception { System.out.println("Inside the test method"); size = driver.manage().window().getSize(); int startx = (int) (size.width * 0.85); int endx = (int) (size.width * 0.20); int starty = size.height / 2; System.out.println(size); WebDriverWait wait = new WebDriverWait(driver, 240); for(int i=1;i<=10;i=i+1) { driver.resetApp(); String timestamp = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss:SSS").format(new Date()); System.out.println(timestamp + " Startsession"); // 操作前获取性能数据 System.out.println(timestamp + " 操作前CPU使用率: " + getCpuUsage()); System.out.println(timestamp + " 操作前内存使用率: " + getMemoryUsage()); //Selecting wait.until(ExpectedConditions.elementToBeClickable(By.id("com.androidappname.app:id/sit_url"))); driver.findElementById("com.androidappname.app:id/sit_url").click(); wait.until(ExpectedConditions.elementToBeClickable(By.id("com.androidappname.app:id/txt_title"))); //clicking on the next 3 times System.out.println("Find the text next"); for(int i1=1;i1<=3;i1++) { driver.findElementById("com.androidappname.app:id/txt_next").click(); } System.out.println("Explore as guest"); wait.until(ExpectedConditions.elementToBeClickable(By.id("com.androidappname.app:id/txt_guest"))); Thread.sleep(500); driver.findElementById("com.androidappname.app:id/txt_guest").click(); System.out.println("Access Granting for the location"); wait.until(ExpectedConditions.elementToBeClickable(By.id("com.android.packageinstaller:id/permission_allow_button"))); driver.findElementById("com.android.packageinstaller:id/permission_allow_button").click(); wait.until(ExpectedConditions.elementToBeClickable(By.id("com.androidappname.app:id/ivYasHeader"))); System.out.println("Home Page Screen"); for(int j=1;j<=4;j++) { System.out.println("Swipe"); driver.swipe(startx, starty, endx, starty, 3000); Thread.sleep(500); } Thread.sleep(500); wait.until(ExpectedConditions.elementToBeClickable(By.id("com.androidappname.app:id/ivYasHeader"))); driver.findElementById("com.androidappname.app:id/footerYasImage").click(); Thread.sleep(500); wait.until(ExpectedConditions.elementToBeClickable(By.id("android:id/button2"))); driver.findElementById("android:id/button2").click(); wait.until(ExpectedConditions.elementToBeClickable(By.id("com.androidappname.app:id/whats_on_tv"))); driver.findElementById("com.androidappname.app:id/navigation_whatson").click(); Thread.sleep(500); wait.until(ExpectedConditions.elementToBeClickable(By.id("com.androidappname.app:id/whats_on_tv"))); driver.findElementById("com.androidappname.app:id/navigation_yas_guide").click(); Thread.sleep(500); wait.until(ExpectedConditions.elementToBeClickable(By.id("com.androidappname.app:id/icon_menu"))); driver.findElementById("com.androidappname.app:id/icon_menu").click(); Thread.sleep(500); System.out.println("Clicking on menu Icon"); System.out.print(count++); // 操作后获取性能数据 timestamp = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss:SSS").format(new Date()); System.out.println(timestamp + " 操作后CPU使用率: " + getCpuUsage()); System.out.println(timestamp + " 操作后内存使用率: " + getMemoryUsage()); } } }
五、注意事项
- 命令兼容性:不同Android版本的
top命令输出格式可能有差异,如果grep不到结果,可以换成ps -eo %cpu,cmd | grep %s来获取CPU数据。 - 测试超时:Device Farm有测试总时长限制,确保
newCommandTimeout设置合理,避免测试中途超时。 - 日志查看:测试完成后,在Device Farm控制台的“Logs”标签下可以查看你打印的性能数据和测试日志。
内容的提问来源于stack exchange,提问作者Rajat Singh
相关产品推荐
相关产品推荐

