Article
appium-Android自动化测试
# 参考链接
- https://github.com/appium/appium-desktop
- https://github.com/appium/java-client
- 移动端自动化测试-Appium Desktop
- √工具应用:使用Appium测试Android应用程序/小米计算器进行测试
- Appium-Desktop使用/Java测试代码
- https://www.toolsqa.com/mobile-automation/appium/findelement-and-findelements-commands/
- https://appiumpro.com/editions/44-working-with-web-components-shadow-dom
# 步骤
# 手机端开发这模式需要进行如下设置:
- (可选)不锁定屏幕
- USB调试
- USB安装
- USB调试(安全设置)
# 安装启动Appium Desktop
- 点击编辑配置
设置ANDROID_HOME C:\Android\Sdk
-
启动服务
-
使用/应用
按步骤操作,先用[检查器会话]的执行一次。这里会给手机安装两个应用,最开始开发者模式的设置就是为安装应用来修改的。
3.1 启动检查器会话
启动服务后,会进入到黑窗口日志界面。点击界面右上角第一个放大镜按钮:[启动检查器会话],编辑[所需功能]
{
"platformName": "Android",
"platformVersion": "7.1",
"appPackage": "com.miui.calculator",
"appActivity": ".cal.CalculatorActivity"
}
如果不需要录制脚本,可以使用 C:\Android\Sdk\tools\bin\uiautomatorviewer.bat 查看也可以的。
3.2 Java客户端
public static final String APP_ID = "com.taobao.idlefish";
private static AppiumDriverLocalService service;
private static AndroidDriver<AndroidElement> driver;
private static WebDriverWait wait;
private void initialization() throws MalformedURLException {
initialization(APP_ID, "com.taobao.idlefish.router.JumpActivity", true, false);
}
private void initialization1() throws MalformedURLException {
initialization("com.miui.calculator", ".cal.CalculatorActivity", null, null);
}
/**
* @param noReset null will remove from the caps.
* @param autoLaunch null will remove from the caps.
*/
protected void initialization(String appPackage, String appActivity, Boolean noReset, Boolean autoLaunch) throws MalformedURLException {
URL url = new URL("http://localhost:4723/wd/hub");
DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.setCapability("automationName", "UiAutomator2" /* "Appium" */);
capabilities.setCapability(MobileCapabilityType.PLATFORM_NAME, "Android");
capabilities.setCapability(MobileCapabilityType.PLATFORM_VERSION, "7.1");
capabilities.setCapability("appPackage", appPackage);
capabilities.setCapability("appActivity", appActivity);
// http://appium.io/docs/en/writing-running-appium/caps/
capabilities.setCapability("noReset", noReset);
capabilities.setCapability("autoLaunch", autoLaunch);
capabilities.setCapability(MobileCapabilityType.NEW_COMMAND_TIMEOUT, 100);
driver = new AndroidDriver<>(url, capabilities);
wait = new WebDriverWait(driver, 20);
}
protected void finishing() {
try {
if (driver != null) {
driver.quit();
}
if (service != null) {
service.stop();
}
} catch (Exception e) {
} finally {
driver = null;
service = null;
}
}
protected void swiping() {
Dimension windowSize = getSize();
int startX = windowSize.width / 2;
int startY = (int) (windowSize.height * 0.8);
int endX = windowSize.width / 2;
int endY = (int) (windowSize.height * 0.1);
swipe(startX, startY, endX, endY, 1200);
}
protected void swipe(int x1, int y1, int x2, int y2, int waitMillis) {
new TouchAction<>(driver) //
.press(ElementOption.point(x1, y1))
.waitAction(WaitOptions.waitOptions(Duration.ofMillis(waitMillis)))
.moveTo(ElementOption.point(x2, y2))
.release()
.perform();
}
protected void touch(int x, int y) {
new TouchAction<>(driver).tap(TapOptions.tapOptions().withPosition(PointOption.point(x, y))).perform();
}
protected void touch(Point p) {
touch(p.x, p.y);
}
protected Dimension getSize() {
return driver.manage().window().getSize();
}
protected Point getCenter() {
Dimension size = getSize();
return new Point(size.width / 2, size.height / 2);
}
private void touchAndWait() throws InterruptedException {
touchAndWait(null);
}
private void touchAndWait(By by) throws InterruptedException {
// 等一下界面效果
// Thread.sleep(500);
TimeUnit.MILLISECONDS.sleep(500);
try {
swiping();
} catch (Exception e) {
touch(getCenter());
}
if (by != null) {
wait.until(ExpectedConditions.presenceOfElementLocated(by));
}
}
private void doCalculator() throws InterruptedException {
By btn_9_s = By.id("com.miui.calculator:id/btn_9_s");
By btn_plus_s = By.id("com.miui.calculator:id/btn_plus_s");
By btn_3_s = By.id("com.miui.calculator:id/btn_3_s");
By btn_equal_s = By.id("com.miui.calculator:id/btn_equal_s");
By textViews = By.className("android.widget.TextView");
touchAndWait(btn_9_s);
driver.findElement(btn_9_s).click();
// driver.findElementByAccessibilityId("加");
driver.findElement(btn_plus_s).click();
driver.findElement(btn_3_s).click();
driver.findElement(btn_equal_s).click();
touchAndWait(textViews);
List<? extends WebElement> allText = driver.findElements(textViews);
boolean isOk = false;
for (int i = 0; i < allText.size(); i++) {
String txt = allText.get(i).getText();
System.out.println(txt);
// 由于第一次无法根据元素的ClassName确定顺序,所以使用遍历的方式进行断言
if (txt.equals("= 12")) {
isOk = true;
break;
}
}
if (isOk) {
System.out.println("测试成功.");
} else {
System.out.println("测试失败.");
}
}
public void testCalculator() throws Exception {
try {
initialization1();
doCalculator();
} finally {
finishing();
}
}
public void testHello() throws InterruptedException, MalformedURLException {
try {
this.initialization1();
doCalculator();
driver.activateApp(APP_ID);
// driver.startActivity(new Activity(APP_ID, "com.taobao.idlefish.router.JumpActivity"));
test0();
test1();
} finally {
this.finishing();
}
}
private String toString(Rectangle rect) {
return String.format("(%s,%s,%s,%s)", rect.x, rect.y, rect.width, rect.height);
}
private void printAllView() throws Exception {
try {
List<AndroidElement> allText = driver.findElementsByXPath("//*");
for (int i = 0; i < allText.size(); i++) {
AndroidElement ele = allText.get(i);
System.out.println("===========================");
System.out.println(ele.getTagName() + ","
+ ele.isSelected() + ","
+ ele.isEnabled() + ","
+ ele.isDisplayed() + ","
+ ele.getText() + ","
+ toString(ele.getRect()));
System.out.println(ele.getAttribute("class"));
System.out.println(ele.getAttribute("content-desc"));
System.out.println(ele.getAttribute("text"));
}
} catch (Exception e) {
String errMsg = e.getMessage();
System.err.println(errMsg);
if (errMsg.contains("A session is either terminated or not started")) {
try {
this.finishing();
} catch (Exception ex) {
}
this.initialization();
}
}
}
private void doProcessManual() throws Exception {
while (true) {
printAllView();
swiping();
Thread.sleep(1000);
}
}
public void testXianyu() throws Exception {
try {
this.initialization();
doProcessManual();
} finally {
this.finishing();
}
}
private void test0() {
System.out.println("appId: " + driver.getCurrentPackage());
System.out.println("activity: " + driver.currentActivity());
System.out.println(APP_ID + " installed?: " + driver.isAppInstalled(APP_ID));
// driver.runAppInBackground(Duration.ofSeconds(-1));
// driver.activateApp(appId);
// driver.closeApp();
// driver.resetApp();
// driver.rotate(new DeviceRotation(0, 0, 0));
// driver.launchApp();
}
private void test1() {
System.out.println("AppStringMap size: " + driver.getAppStringMap().size());
System.out.println("en AppStringMap size: " + driver.getAppStringMap("en").size());
System.out.println("build: " + driver.getStatus().get("build"));
System.out.println("DeviceTime: " + driver.getDeviceTime());
}
–END
Related
Related posts
-
Windows搭建Flutter桌面开发环境一步到位
2025-06-02
-
被 n8n 任务 60s 超时反复折磨后的排查记录:从报错、堆栈到源码,理清 TASK_TIMEOUT 的真实用途与位置
2026-01-10
-
跟着 n8n 案例来学习子流程 Sub-workflow
2025-12-31
-
n8n 终于还是部署到 Docker 了,经验就是要反反复复地去验证:要想少走弯路,就按官方推荐的最佳实践
2025-12-29