20220724 app自动化测试实战一

实战目标

  • 企业微信的用户管理

课堂ppt

PPT地址

app启动验证

#adb shell am start -W -S 包名/activity

adb shell am start -W -S com.tencent.wework/.launch.LaunchSplashActivity

知识汇总

2022_App自动化测试学习大纲.xmind.zip (248.9 KB)

pom文件


    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.encoding>UTF-8</maven.compiler.encoding>
        <java.version>11</java.version>
        <!-- 使用 Java 11 语言特性 ( -source 11 ) 并且还希望编译后的类与 JVM 11 ( -target 11 )兼容,您可以添加以下两个属性,它们是默认属性插件参数的名称-->
        <maven.compiler.target>11</maven.compiler.target>
        <maven.compiler.source>11</maven.compiler.source>
        <!-- 对应junit Jupiter的版本号;放在这里就不需要在每个依赖里面写版本号,导致对应版本号会冲突-->
        <junit.jupiter.version>5.8.2</junit.jupiter.version>
        <!-- plugins -->
        <maven.compiler.version>3.8.1</maven.compiler.version>
        <maven-surefire-plugin.version>3.0.0-M5</maven-surefire-plugin.version>
        <!-- 断言-->
        <hamcrest.version>2.2</hamcrest.version>
        <!--log日志-->
        <slf4j.version>2.0.0-alpha7</slf4j.version>
        <logback.version>1.3.0-alpha16</logback.version>
        <!--allure报告-->
        <allure.version>2.18.1</allure.version>
        <allure-maven.version>2.10.0</allure-maven.version>
        <allure.cmd.download.url>
            https://repo.maven.apache.org/maven2/io/qameta/allure/allure-commandline
        </allure.cmd.download.url>
        <aspectj.version>1.9.9.1</aspectj.version>
        <!-- faker -->
        <javafaker.version>1.0.2</javafaker.version>
        <!-- selenium -->
        <selenium.version>4.3.0</selenium.version>
        <!-- appium -->
        <appium.version>8.1.1</appium.version>
        <jackson.version>2.13.1</jackson.version>
        <commonio.version>2.11.0</commonio.version>
    </properties>
    <!--    物料清单 (BOM)-->
    <dependencyManagement>
        <dependencies>
            <!--当使用 Gradle 或 Maven 引用多个 JUnit 工件时,此物料清单 POM 可用于简化依赖项管理。不再需要在添加依赖时设置版本-->
            <dependency>
                <groupId>org.junit</groupId>
                <artifactId>junit-bom</artifactId>
                <version>${junit.jupiter.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>com.google.guava</groupId>
                <artifactId>guava</artifactId>
                <version>31.0.1-jre</version>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <repositories>
        <repository>
            <id>jitpack.io</id>
            <url>https://jitpack.io</url>
        </repository>
    </repositories>

    <dependencies>
        <!--junit5-->
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <!--对应添加的依赖的作用范围-->
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.platform</groupId>
            <artifactId>junit-platform-suite</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.vintage</groupId>
            <artifactId>junit-vintage-engine</artifactId>
            <scope>test</scope>
        </dependency>
        <!--hamcrest断言-->
        <dependency>
            <groupId>org.hamcrest</groupId>
            <artifactId>hamcrest</artifactId>
            <version>${hamcrest.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.hamcrest</groupId>
            <artifactId>hamcrest-core</artifactId>
            <version>${hamcrest.version}</version>
            <scope>test</scope>
        </dependency>
        <!--faster json解析yaml文件 -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>${jackson.version}</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.dataformat</groupId>
            <artifactId>jackson-dataformat-yaml</artifactId>
            <version>${jackson.version}</version>
            <scope>compile</scope>
        </dependency>
        <!-- log日志 -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>${slf4j.version}</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>${logback.version}</version>
        </dependency>

        <!-- common io-->
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>${commonio.version}</version>
        </dependency>
        <!-- allure-->
        <dependency>
            <groupId>io.qameta.allure</groupId>
            <artifactId>allure-junit5</artifactId>
            <version>${allure.version}</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>${aspectj.version}</version>
            <scope>runtime</scope>
        </dependency>
        <!--  随机测试数据生成库  -->
        <dependency>
            <groupId>com.github.javafaker</groupId>
            <artifactId>javafaker</artifactId>
            <version>${javafaker.version}</version>
        </dependency>
        <!-- selenium-->
        <dependency>
            <groupId>org.seleniumhq.selenium</groupId>
            <artifactId>selenium-java</artifactId>
            <version>${selenium.version}</version>
        </dependency>
        <!-- appium -->
        <dependency>
            <groupId>io.appium</groupId>
            <artifactId>java-client</artifactId>
            <version>${appium.version}</version>
            <scope>compile</scope>
        </dependency>


    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>${maven-surefire-plugin.version}</version>
                <configuration>
                    <argLine>
                        -javaagent:"${settings.localRepository}/org/aspectj/aspectjweaver/${aspectj.version}/aspectjweaver-${aspectj.version}.jar"
                    </argLine>
                    <includes>
                        <include>**/*Test</include>
                    </includes>
                </configuration>
                <dependencies>
                    <dependency>
                        <groupId>org.junit.jupiter</groupId>
                        <artifactId>junit-jupiter-engine</artifactId>
                        <version>${junit.jupiter.version}</version>
                    </dependency>
                    <dependency>
                        <groupId>org.junit.vintage</groupId>
                        <artifactId>junit-vintage-engine</artifactId>
                        <version>${junit.jupiter.version}</version>
                    </dependency>
                </dependencies>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>${maven.compiler.version}</version>
                <configuration>
                    <parameters>true</parameters>
                    <!-- 设置jre版本为 11 -->
                    <source>${maven.compiler.source}</source>
                    <target>${maven.compiler.target}</target>
                    <!-- 设置编码为 UTF-8 -->
                    <encoding>${maven.compiler.encoding}</encoding>
                </configuration>
            </plugin>

            <plugin>
                <groupId>io.qameta.allure</groupId>
                <artifactId>allure-maven</artifactId>
                <version>${allure-maven.version}</version>
                <configuration>
                    <reportVersion>${allure.version}</reportVersion>
                    <allureDownloadUrl>${allure.cmd.download.url}/${allure.version}/allure-commandline-${allure.version}.zip</allureDownloadUrl>
                </configuration>
            </plugin>
        </plugins>
    </build>
       DesiredCapabilities desiredCapabilities = new DesiredCapabilities();
        //平台名称 安卓系统就是Android 苹果手机就是iOS
        desiredCapabilities.setCapability(MobileCapabilityType.PLATFORM_NAME, "Android");
        //使用的driver uiautomator2
        desiredCapabilities.setCapability(MobileCapabilityType.AUTOMATION_NAME, "UiAutomator2");
        //设备的系统版本
        desiredCapabilities.setCapability(MobileCapabilityType.PLATFORM_VERSION, "12.0.0");
        //启动的app的包名
        desiredCapabilities.setCapability(AndroidMobileCapabilityType.APP_PACKAGE, "com.tencent.wework");
        //启动的app的页面
        desiredCapabilities.setCapability(AndroidMobileCapabilityType.APP_ACTIVITY, ".launch.LaunchSplashActivity");
        //设备名称
        desiredCapabilities.setCapability(MobileCapabilityType.DEVICE_NAME, "盖盖");
        //设备的UDID;adb devices -l 获取,多设备的时候要指定,若不指定默认选择列表的第一个设备
        desiredCapabilities.setCapability(MobileCapabilityType.UDID, "8c5f5f92");
        //app不重置
        desiredCapabilities.setCapability(MobileCapabilityType.NO_RESET, true);
        //运行失败的时候打印page source到appium-log
        desiredCapabilities.setCapability(MobileCapabilityType.PRINT_PAGE_SOURCE_ON_FIND_FAILURE, true);
        //在假设客户端退出并结束会话之前,Appium 将等待来自客户端的新命令多长时间(以秒为单位) http请求等待响应最长5分钟
        desiredCapabilities.setCapability(MobileCapabilityType.NEW_COMMAND_TIMEOUT, 300000);
        //默认权限通过
        desiredCapabilities.setCapability(SupportsAutoGrantPermissionsOption.AUTO_GRANT_PERMISSIONS_OPTION, true);

打卡

desiredCapabilities.setCapability(SupportsWaitForIdleTimeoutOption.WAIT_FOR_IDLE_TIMEOUT_OPTION, 0);

css定位

注意:warning:
id转为css:需要转义,\代表专一

UiSelector

https://developer.android.com/reference/androidx/test/uiautomator/UiSelector

作业

作业一

添加成员优化:

toast元素验证
添加log日志
添加截图
添加allure报告

作业二

删除成员用例编写

  • 多用css定位

最好是id,xpth相对定位,css定位都在脚本内使用

作业一:toast元素验证,添加log日志,添加截图,添加allure报告
脚本添加toast元素验证,添加截图
package com.ceshiren.sample;

import com.ceshiren.until.FakerUtil;
import com.ceshiren.until.SaveScreenUtil;
import io.appium.java_client.AppiumBy;
import io.appium.java_client.android.AndroidDriver;
import io.appium.java_client.android.options.app.SupportsAutoGrantPermissionsOption;
import io.appium.java_client.remote.AndroidMobileCapabilityType;
import io.appium.java_client.remote.MobileCapabilityType;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.Dimension;
import org.openqa.selenium.Point;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.interactions.PointerInput;
import org.openqa.selenium.interactions.Sequence;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import java.net.MalformedURLException;
import java.net.URL;
import java.time.Duration;
import java.util.Arrays;

import static java.lang.Thread.sleep;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
import static org.junit.jupiter.api.Assertions.assertAll;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.openqa.selenium.interactions.PointerInput.MouseButton.LEFT;

public class AddMemberPOTest {

public static AndroidDriver driver;

public static WebDriverWait wait;

@BeforeAll
public static void setUp(){

    try {

        DesiredCapabilities desiredCapabilities = new DesiredCapabilities();
        //平台名称 安卓系统就是Android 苹果手机就是iOS
        desiredCapabilities.setCapability(MobileCapabilityType.PLATFORM_NAME, "Android");
        //使用的driver uiautomator2
        desiredCapabilities.setCapability(MobileCapabilityType.AUTOMATION_NAME, "UiAutomator2");
        //设备的系统版本
        desiredCapabilities.setCapability(MobileCapabilityType.PLATFORM_VERSION, "8.1.0");
        //启动的app的包名,获取第三方的包名 adb shell pm list package -3
        desiredCapabilities.setCapability(AndroidMobileCapabilityType.APP_PACKAGE, "com.tencent.wework");
        //启动的app的页面,获取app的启动页面 adb shell monkey -p <package_name> -vvv 1
        desiredCapabilities.setCapability(AndroidMobileCapabilityType.APP_ACTIVITY, ".launch.LaunchSplashActivity");
        //设备名称
        desiredCapabilities.setCapability(MobileCapabilityType.DEVICE_NAME, "xiaochen");
        //设备的UDID;adb devices -l 获取,多设备的时候要指定,若不指定默认选择列表的第一个设备
        desiredCapabilities.setCapability(MobileCapabilityType.UDID, "192.168.71.21:5555");
        //app不重置
        desiredCapabilities.setCapability(MobileCapabilityType.NO_RESET, true);
        //运行失败的时候打印page source到appium-log,这里的appium-log指的是打印到appium server
        desiredCapabilities.setCapability(MobileCapabilityType.PRINT_PAGE_SOURCE_ON_FIND_FAILURE, true);
        //在假设客户端退出并结束会话之前,Appium 将等待来自客户端的新命令多长时间(以秒为单位) http请求等待响应最长5分钟
        desiredCapabilities.setCapability(MobileCapabilityType.NEW_COMMAND_TIMEOUT, 300000);
        //默认权限通过
        desiredCapabilities.setCapability(SupportsAutoGrantPermissionsOption.AUTO_GRANT_PERMISSIONS_OPTION, true);
        //连接本地的appium server
        URL remoteUrl = new URL("http://127.0.0.1:4723/wd/hub");
        //打开手机端的企业微信
        driver = new AndroidDriver(remoteUrl,desiredCapabilities);
        //隐式等待
        driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10));
        //显示等待
        wait = new WebDriverWait(driver,Duration.ofSeconds(5));

    } catch (MalformedURLException e) {
        e.printStackTrace();
    }
}

//cssSelector定位,添加成员
@Test
public void addMemberByCssSelector(){

    //点击通讯录
    By contactBy = AppiumBy.cssSelector("[text=\"通讯录\"]");
    WebElement contactButn = driver.findElement(contactBy);
    SaveScreenUtil.saveScreen(driver,contactBy.toString());
    contactButn.click();

    //通讯录页面的成员太多,导致添加成员按钮不在可见范围内
    //滑动到添加成员
    //1.获取滑动页面的长宽
    Dimension dimension = driver.manage().window().getSize();
    //滑动的启始坐标位置
    Point start = new Point((int) (dimension.width*0.5),(int) (dimension.height*0.9));
    //滑动的目标坐标位置
    Point end = new Point((int) (dimension.width*0.5),(int) (dimension.height*0.3));

    //2.指定用什么滑动手机的页面,别名为finger手指
    PointerInput FINGER = new PointerInput(PointerInput.Kind.TOUCH,"finger");

    //这里相当于在指定的位置按下鼠标
    Sequence sequence = new Sequence(FINGER,1)
            .addAction(FINGER.createPointerMove(
                            Duration.ofMillis(0),//只是指定滑动开始位置,不用等待
                            PointerInput.Origin.viewport(),
                            start.getX(),start.getY()
                    )
            )
            .addAction(FINGER.createPointerDown(LEFT.asArg()))//按下鼠标左键
            //这里相当于在目标位置抬起鼠标
            .addAction(FINGER.createPointerMove(
                            Duration.ofMillis(1000),//滑动页面需要时间,等1s
                            PointerInput.Origin.viewport(),
                            end.getX(),end.getY()
                    )
            )
            .addAction(FINGER.createPointerUp(LEFT.asArg()));//抬起鼠标左键

    driver.perform(Arrays.asList(sequence));

    //强等5秒,为了后期的脚本执行过程中的稳定性
    try {
        sleep(5000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

    //通讯录页面,点击添加成员,跳转到添加成员页面
    By addMemberby = AppiumBy.cssSelector("[text=\"添加成员\"]");
    WebElement addMemberButn = driver.findElement(addMemberby);
    SaveScreenUtil.saveScreen(driver,addMemberby.toString());
    addMemberButn.click();

    //点击手动添加,跳转到编辑成员页面
    By fingerAddMemberby = AppiumBy.cssSelector("[text=\"手动输入添加\"]");
    WebElement fingerAddMemberButn = driver.findElement(fingerAddMemberby);
    SaveScreenUtil.saveScreen(driver,fingerAddMemberby.toString());
    fingerAddMemberButn.click();

    //使用cssSeletor方法用id进行定位,需要转换特殊符号,因为cssSelector方法的底层用的正则匹配的方式
    //编辑成员页面,输入姓名
    //cssSelector用id等定位,不注销特殊符号,那么appiumServer底层在转换原生定位时会报错,原因是拼接成了"new UiSelector().resourceId(\"android:id/com\")
    //WebElement inputUnameEle = driver.findElement(AppiumBy.cssSelector("#com"));
    By nameInputBy = AppiumBy.cssSelector("#com\\.tencent\\.wework\\:id\\/bx0");
    WebElement nameInput = driver.findElement(nameInputBy);
    SaveScreenUtil.saveScreen(driver,nameInputBy.toString());
    nameInput.clear();
    String uname = FakerUtil.get_name();
    nameInput.sendKeys(uname);

    //编辑成员页面,输入手机号
    By inputMobileBy = AppiumBy.cssSelector("#com\\.tencent\\.wework\\:id\\/i1u");
    WebElement mobileInput = driver.findElement(inputMobileBy);
    SaveScreenUtil.saveScreen(driver,inputMobileBy.toString());
    mobileInput.clear();
    String iphone = FakerUtil.get_zh_phone();
    mobileInput.sendKeys(iphone);

    //编辑成员页面,点击保存,跳转到添加成员页面
    By saveButnBy = AppiumBy.cssSelector("[resource-id=\"com.tencent.wework:id/awx\"]");
    WebElement saveButn = driver.findElement(saveButnBy);
    SaveScreenUtil.saveScreen(driver,saveButnBy.toString());
    saveButn.click();

    //获取Toast消息
    String toastValue = driver.findElement(AppiumBy.xpath("//*[@class=\"android.widget.Toast\"]")).getText();

    //添加成员页面,点击返回按钮,跳转到通讯录页面
    By backButnBy = AppiumBy.cssSelector("#com\\.tencent\\.wework\\:id\\/l1u");
    WebElement backButn = driver.findElement(backButnBy);
    SaveScreenUtil.saveScreen(driver,backButnBy.toString());
    backButn.click();

    //进入断言阶段,用例验证

    //通讯录页面,点击搜索button,跳转到搜索页面
    By searchButnBy = AppiumBy.cssSelector("[resource-id=\"com.tencent.wework:id/l2q\"]");
    WebElement searchButn = driver.findElement(searchButnBy);
    SaveScreenUtil.saveScreen(driver,searchButnBy.toString());
    searchButn.click();

    //搜索页面,点击搜索框输入用户名,跳转到搜索结果页面
    By searchInputBy = AppiumBy.cssSelector("[resource-id=\"com.tencent.wework:id/jca\"]");
    WebElement searchInput = driver.findElement(searchInputBy);
    SaveScreenUtil.saveScreen(driver,searchButnBy.toString());
    searchInput.clear();
    searchInput.sendKeys(uname);
    searchInput.click();

    //在搜索结果页面,点击第一条搜索结果,跳转到用户详情页面
    //第一种定位方式,这种方式更推荐,更灵活,使用find.Element
    wait.until(ExpectedConditions.visibilityOfElementLocated(
            AppiumBy.xpath("//*[@text=\"联系人\"]/../following-sibling::*//*[@class=\"android.widget.TextView\"]"))).click();

    //第二种定位方式
    /*WebElement searchUserName = wait.until(ExpectedConditions.visibilityOfElementLocated(AppiumBy.xpath("//*[@resource-id=\"com.tencent.wework:id/f37\"]/android.view.ViewGroup/android.widget.TextView")));
    String searchUname = searchUserName.getText();*/

    //在用户详情页面,获取姓名的text文本
    By userNameBy = AppiumBy.cssSelector("#com\\.tencent\\.wework\\:id\\/ke0");
    WebElement userName = driver.findElement(userNameBy);
    SaveScreenUtil.saveScreen(driver,userNameBy.toString());
    String active_uname = userName.getText();

    //在用户详情页面点击返回按钮,返回到搜索页面
    By backButnBy2 = AppiumBy.cssSelector("#com\\.tencent\\.wework\\:id\\/l1u");
    WebElement backButn2 = driver.findElement(backButnBy2);
    SaveScreenUtil.saveScreen(driver,backButnBy2.toString());
    backButn2.click();

    //在搜索页面点击返回按钮,返回到通讯录页面
    By backButnBy3 = AppiumBy.cssSelector("#com\\.tencent\\.wework\\:id\\/l1u");
    WebElement backButn3 = driver.findElement(backButnBy3);
    SaveScreenUtil.saveScreen(driver,backButn3.toString());
    backButn3.click();

    //多断言
    assertAll("测试添加成员失败",
            ()->assertTrue(toastValue.contains("添加成功")),
            ()->assertThat("搜索不到添加的新成员",uname,is(equalTo(active_uname))));
}

@AfterAll
public static void tearDown(){
    driver.quit();
}

}

配置日志等级,添加allure报告
日志等级


添加allure报告,allure报告结果
image

作业二:删除成员用例编写
package com.ceshiren.sample;

import com.ceshiren.until.FakerUtil;
import io.appium.java_client.AppiumBy;
import io.appium.java_client.android.AndroidDriver;
import io.appium.java_client.android.options.app.SupportsAutoGrantPermissionsOption;
import io.appium.java_client.remote.AndroidMobileCapabilityType;
import io.appium.java_client.remote.MobileCapabilityType;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.openqa.selenium.By;
import org.openqa.selenium.Dimension;
import org.openqa.selenium.Point;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.interactions.PointerInput;
import org.openqa.selenium.interactions.Sequence;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;

import java.net.MalformedURLException;
import java.net.URL;
import java.time.Duration;
import java.util.Arrays;
import java.util.stream.Stream;

import static java.lang.Thread.sleep;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.openqa.selenium.interactions.PointerInput.MouseButton.LEFT;

public class DeleteMemberPOTest {

public static WebDriverWait wait;

public static AndroidDriver driver;

@BeforeAll
public static void delteMemberInit(){
    try {

        DesiredCapabilities desiredCapabilities = new DesiredCapabilities();
        //平台名称 安卓系统就是Android 苹果手机就是iOS
        desiredCapabilities.setCapability(MobileCapabilityType.PLATFORM_NAME, "Android");
        //使用的driver uiautomator2
        desiredCapabilities.setCapability(MobileCapabilityType.AUTOMATION_NAME, "UiAutomator2");
        //设备的系统版本
        desiredCapabilities.setCapability(MobileCapabilityType.PLATFORM_VERSION, "8.1.0");
        //启动的app的包名,获取第三方的包名 adb shell pm list package -3
        desiredCapabilities.setCapability(AndroidMobileCapabilityType.APP_PACKAGE, "com.tencent.wework");
        //启动的app的页面,获取app的启动页面 adb shell monkey -p <package_name> -vvv 1
        desiredCapabilities.setCapability(AndroidMobileCapabilityType.APP_ACTIVITY, ".launch.LaunchSplashActivity");
        //设备名称
        desiredCapabilities.setCapability(MobileCapabilityType.DEVICE_NAME, "xiaochen");
        //设备的UDID;adb devices -l 获取,多设备的时候要指定,若不指定默认选择列表的第一个设备
        desiredCapabilities.setCapability(MobileCapabilityType.UDID, "192.168.71.21:5555");
        //app不重置
        desiredCapabilities.setCapability(MobileCapabilityType.NO_RESET, true);
        //运行失败的时候打印page source到appium-log,这里的appium-log指的是打印到appium server
        desiredCapabilities.setCapability(MobileCapabilityType.PRINT_PAGE_SOURCE_ON_FIND_FAILURE, true);
        //在假设客户端退出并结束会话之前,Appium 将等待来自客户端的新命令多长时间(以秒为单位) http请求等待响应最长5分钟
        desiredCapabilities.setCapability(MobileCapabilityType.NEW_COMMAND_TIMEOUT, 300000);
        //默认权限通过
        desiredCapabilities.setCapability(SupportsAutoGrantPermissionsOption.AUTO_GRANT_PERMISSIONS_OPTION, true);
        //连接本地的appium server
        URL remoteUrl = new URL("http://127.0.0.1:4723/wd/hub");
        //打开手机端的企业微信
        driver = new AndroidDriver(remoteUrl,desiredCapabilities);
        //隐式等待
        driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10));
        //显示等待
        wait = new WebDriverWait(driver,Duration.ofSeconds(5));

    } catch (MalformedURLException e) {
        e.printStackTrace();
    }
}

//删除成员
@ParameterizedTest
@MethodSource("delMemberTestParameter")
public void delMemberTest(String name){

    //通讯录页面,点击搜索按钮,跳转到搜索页面
    driver.findElement(AppiumBy.cssSelector("[resource-id=\"com.tencent.wework:id/l2q\"]")).click();

    //在搜索页面,点击搜索框,输入用户,到搜索结果页面
    WebElement searchInputEle = wait.until(ExpectedConditions.visibilityOfElementLocated(AppiumBy.cssSelector("[text=\"搜索\"]")));
    searchInputEle.clear();
    searchInputEle.sendKeys(name);

    //在搜索结果页面,点击第一个结果,跳转到用户详情页面
    driver.findElement(AppiumBy.xpath("//*[@text=\"联系人\"]/../following-sibling::*//*[@class=\"android.widget.TextView\"]")).click();

    //在用户详情页面,点击右上角的设置,跳转到设置页面
    driver.findElement(AppiumBy.cssSelector("[resource-id=\"com.tencent.wework:id/l2f\"]")).click();

    //在设置页面,点击编辑按钮,跳转到编辑页面
    driver.findElement(AppiumBy.cssSelector("[text=\"编辑成员\"]")).click();

    //在编辑页面,等待设置部门
    wait.until(driver1 -> {
         return driver1.findElement(AppiumBy.cssSelector("[text=\"设置部门\"]"));
    });

    //在编辑页面,滑动到删除成员
    //1.获取滑动页面的长宽
    Dimension dimension = driver.manage().window().getSize();
    //滑动的启始坐标位置
    Point start = new Point((int) (dimension.width*0.5),(int) (dimension.height*0.9));
    //滑动的目标坐标位置
    Point end = new Point((int) (dimension.width*0.5),(int) (dimension.height*0.3));

    //2.指定用什么滑动手机的页面,别名为finger手指
    PointerInput FINGER = new PointerInput(PointerInput.Kind.TOUCH,"finger");

    //这里相当于在指定的位置按下鼠标
    Sequence sequence = new Sequence(FINGER,1)
            .addAction(FINGER.createPointerMove(
                            Duration.ofMillis(0),//只是指定滑动开始位置,不用等待
                            PointerInput.Origin.viewport(),
                            start.getX(),start.getY()
                    )
            )
            .addAction(FINGER.createPointerDown(LEFT.asArg()))//按下鼠标左键
            //这里相当于在目标位置抬起鼠标
            .addAction(FINGER.createPointerMove(
                            Duration.ofMillis(1000),//滑动页面需要时间,等1s
                            PointerInput.Origin.viewport(),
                            end.getX(),end.getY()
                    )
            )
            .addAction(FINGER.createPointerUp(LEFT.asArg()));//抬起鼠标左键

    driver.perform(Arrays.asList(sequence));

    //强等3秒,为了后期的脚本执行过程中的稳定性
    try {
        sleep(3000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

    //在编辑页面,点击删除成员按钮
    driver.findElement(AppiumBy.cssSelector("[text=\"删除成员\"]")).click();

    //在编辑页面,点击确定,跳转至搜索结果页面
    wait.until(ExpectedConditions.visibilityOfElementLocated(AppiumBy.cssSelector("[resource-id=\"com.tencent.wework:id/cja\"]"))).click();

    //在搜索结果页面,点击返回,回到通讯录页面
    driver.findElement(AppiumBy.cssSelector("[resource-id=\"com.tencent.wework:id/l1u\"]")).click();

    //进入断言阶段
    //在通讯录页面,点击搜索按钮,跳转至搜索页面
    driver.findElement(AppiumBy.cssSelector("[resource-id=\"com.tencent.wework:id/l2q\"]")).click();

    //在搜索页面,点击搜索框,输入用户名,跳转至搜索结果页面
    searchInputEle = driver.findElement(AppiumBy.cssSelector("[resource-id=\"com.tencent.wework:id/jca\"]"));
    searchInputEle.clear();
    searchInputEle.sendKeys(name);

    //在搜索结果页面,获取"无搜索结果"
    String text = driver.findElement(AppiumBy.cssSelector("[resource-id=\"com.tencent.wework:id/dfw\"]")).getText();

    //在搜索结果页面,点击返回按钮,回到通讯录页面
    driver.findElement(AppiumBy.cssSelector("[resource-id=\"com.tencent.wework:id/l1u\"]")).click();

    //断言
    assertThat("删除成员,测试失败",text,is(equalTo("无搜索结果")));

}

static Stream<Arguments> delMemberTestParameter(){
    
	//添加成员的参数
    //用户名
    String name = FakerUtil.get_name();
    //手机号
    String mobile = FakerUtil.get_zh_phone();

    //消息页面,点击通讯录,跳转到通讯页面
    By contactBy = AppiumBy.cssSelector("[text=\"通讯录\"]");
    WebElement contactButn = driver.findElement(contactBy);
    contactButn.click();

    //滑动到添加成员
    Dimension dimension = driver.manage().window().getSize();
   
    Point start = new Point((int) (dimension.width*0.5),(int) (dimension.height*0.9));
    
    Point end = new Point((int) (dimension.width*0.5),(int) (dimension.height*0.3));

    
    PointerInput FINGER = new PointerInput(PointerInput.Kind.TOUCH,"finger");

    //这里相当于在启始位置按下鼠标
    Sequence sequence = new Sequence(FINGER,1)
            .addAction(FINGER.createPointerMove(
                            Duration.ofMillis(0),
                            PointerInput.Origin.viewport(),
                            start.getX(),start.getY()
                    )
            )
            .addAction(FINGER.createPointerDown(LEFT.asArg()))//按下鼠标左键
            //这里相当于在目标位置抬起鼠标
            .addAction(FINGER.createPointerMove(
                            Duration.ofMillis(1000),//滑动页面需要时间,等1s
                            PointerInput.Origin.viewport(),
                            end.getX(),end.getY()
                    )
            )
            .addAction(FINGER.createPointerUp(LEFT.asArg()));//抬起鼠标左键

    driver.perform(Arrays.asList(sequence));

    //强等3秒
    try {
        sleep(3000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

    //通讯录页面,点击添加成员,跳转到添加成员页面
    By addMemberby = AppiumBy.cssSelector("[text=\"添加成员\"]");
    WebElement addMemberButn = driver.findElement(addMemberby);
    addMemberButn.click();

    //添加成员页面,点击手动添加,跳转到编辑成员页面
    By fingerAddMemberby = AppiumBy.cssSelector("[text=\"手动输入添加\"]");
    WebElement fingerAddMemberButn = driver.findElement(fingerAddMemberby);
    fingerAddMemberButn.click();
    
	//输入姓名 
    By nameInputBy = AppiumBy.cssSelector("#com\\.tencent\\.wework\\:id\\/bx0");
    WebElement nameInput = driver.findElement(nameInputBy);
    nameInput.clear();
    nameInput.sendKeys(name);

    //输入手机号
    By inputMobileBy = AppiumBy.cssSelector("#com\\.tencent\\.wework\\:id\\/i1u");
    WebElement mobileInput = driver.findElement(inputMobileBy);
    mobileInput.clear();
    mobileInput.sendKeys(mobile);

    //点击保存
    By saveButnBy = AppiumBy.cssSelector("[resource-id=\"com.tencent.wework:id/awx\"]");
    WebElement saveButn = driver.findElement(saveButnBy);
    saveButn.click();

    //添加成员页面,点击返回按钮,跳转到通讯录页面
    By backButnBy = AppiumBy.cssSelector("#com\\.tencent\\.wework\\:id\\/l1u");
    WebElement backButn = driver.findElement(backButnBy);
    backButn.click();

    return Stream.of(
            Arguments.arguments(name)
    );
}

}