添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
  • 腾讯微信团队打造的企业通讯与办公工具,具有与微信一致的沟通体验,丰富的 OA 应用,和连接微信生态的能力。
  • 可帮助企业连接内部、连接生态伙伴、连接消费者。专业协作、安全管理、人即服务。
  • 前提条件:
  • 手机端安装好企业微信 App。
  • 企业微信注册用户。
  • 企业微信 App 搜索联系人功能自动化。
  • 企业微信 App 添加成员功能自动化测试。
  • 实战 1:搜索联系人测试场景

    Python 实现
    from appium import webdriver
    from appium.webdriver.common.appiumby import AppiumBy
    from appium.options.common import AppiumOptions
    class TestWework:
        def setup(self):
            # Capability 设置定义为字典
            caps = {}
            # 设置 app 安装的平台(Android、iOS)
            caps["platformName"] = "Android"
            # 设置 app 安装平台的版本
            caps["appium:platformVersion"] = "6"
            # 设备的名字
            caps["appium:deviceName"] = "MuMu"
            # 设置 app 的包名
            caps["appium:appPackage"] = "com.tencent.wework"
            # 设置 app 启动页
            caps["appium:appActivity"] = ".launch.LaunchSplashActivity"
            # 不清空缓存
            caps["appium:noReset"] = True
            # app 不重启
            # caps["appium:dontStopAppOnReset"] = True
            options = AppiumOptions().load_capabilities(caps)
            # 初始化 driver
            self.driver = webdriver.Remote("http://127.0.0.1:4723/wd/hub", options=options)
            # 设置全局的隐式等待
            self.driver.implicitly_wait(10)
        def teardown(self):
            # 关闭 driver
            self.driver.quit()
        def test_search_contact(self):
            冒烟用例,搜索存在的联系人
            search_key = "蚊子"
            # 点击通讯录按钮
            self.driver.find_element(
                AppiumBy.XPATH,
                "//*[@text='通讯录']").click()
            # 点击搜索按钮
            self.driver.find_element(
                # ID 定位,id在不同手机上可能会变化,所以不推荐使用
                # AppiumBy.ID,
                # "com.tencent.wework:id/lyp").click()
                AppiumBy.XPATH,
                # xpath 轴定位
                '//*[@text="悠然科技"]/../../../following-sibling::*/*[1]').click()
                # xpath 父子定位
                # '//*[@text="悠然科技"]/../../../../*[3]/*[1]').click()
            # 查找搜索框,输入搜索关键词
            self.driver.find_element(
                AppiumBy.XPATH,
                "//*[@text='搜索']").send_keys(search_key)
            # 获取搜索结果元素的文本,完成断言
            eles = self.driver.find_elements(
                AppiumBy.XPATH,
                '//*[@class="android.widget.ListView"]//*[@class="android.widget.ImageView"]/../../following-sibling::*/*[1]/*[1]')
            assert search_key in [ele.text for ele in eles]
    
    Java 实现
    import io.appium.java_client.AppiumBy;
    import io.appium.java_client.android.AndroidDriver;
    import io.appium.java_client.android.options.UiAutomator2Options;
    import org.junit.jupiter.api.AfterAll;
    import org.junit.jupiter.api.BeforeAll;
    import org.junit.jupiter.api.Test;
    import org.openqa.selenium.WebElement;
    import java.net.MalformedURLException;
    import java.net.URL;
    import java.time.Duration;
    import java.util.List;
    import static org.junit.jupiter.api.Assertions.assertTrue;
    public class TestMember {
        public static AndroidDriver driver;
        @BeforeAll
        public static void setUpClass() {
            //配置信息
            UiAutomator2Options uiAutomator2Options = new UiAutomator2Options()
                    .setPlatformName("Android")
                    .setAutomationName("uiautomator2")
                    .setNoReset(true)
                    .amend("appium:appPackage", "com.tencent.wework")
                    .amend("appium:appActivity", ".launch.LaunchSplashActivity")
                    .amend("appium:forceAppLaunch", true)
                    .amend("appium:shouldTerminateApp", true);
            //初始化
            try {
                driver = new AndroidDriver(new URL("http://127.0.0.1:4723"), uiAutomator2Options);
                driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(5));
            } catch (MalformedURLException e) {
                throw new RuntimeException(e);
        @AfterAll
        public static void tearDownClass(){
            driver.quit();
         * 前置条件: 登录成功
         * 1. 进入通讯录页面
         * 2. 点击搜索按钮,输入一个已经存在的成员名称。
        @Test
        public void searchMember() {
            driver.findElement(AppiumBy.xpath("//*[@text='通讯录']")).click();
            driver.findElement(AppiumBy.xpath("//*[@text='Hufflepuff']/../../../../*[3]/*[1]")).click();
            driver.findElement(AppiumBy.xpath("//*[@text='搜索']")).sendKeys("张三");
            List<WebElement> webElements =  driver.findElements(AppiumBy.xpath("//*[@class='androidx.recyclerview.widget.RecyclerView']//*[@class='android.widget.LinearLayout']//*[@class='android.widget.TextView']"));
            //  1. 遍历所有元素 2. 获取所有元素对应的文本 3. 判断是否包含张三 4. 返回判断结果,如果所有元素都包含,则返回true
            assertTrue(webElements.stream().allMatch(item -> item.getText().contains("张三")));
    

    实战 2:添加联系人测试场景

    Python 实现
    from appium import webdriver
    from appium.webdriver.common.appiumby import AppiumBy
    from appium.options.common import AppiumOptions
    from faker import Faker
    class TestWework:
        def setup_class(self):
            faker = Faker("zh_CN")
            self.name = faker.name()
            self.phonenum = faker.phone_number()
        def setup(self):
            # Capability 设置定义为字典
            caps = {}
            # 设置 app 安装的平台(Android、iOS)
            caps["platformName"] = "Android"
            # 设置 app 安装平台的版本
            caps["appium:platformVersion"] = "6"
            # 设备的名字
            caps["appium:deviceName"] = "MuMu"
            # 设置 app 的包名
            caps["appium:appPackage"] = "com.tencent.wework"
            # 设置 app 启动页
            caps["appium:appActivity"] = ".launch.LaunchSplashActivity"
            # 不清空缓存
            caps["appium:noReset"] = True
            # 实例化 AppiumOptions 对象
            options = AppiumOptions().load_capabilities(caps)
            # 初始化 driver
            self.driver = webdriver.Remote("http://127.0.0.1:4723/wd/hub", options=options)
            # 设置全局的隐式等待
            self.driver.implicitly_wait(10)
        def teardown(self):
            # 关闭 driver
            self.driver.quit()
        def test_add_contact(self):
            添加联系人
            1。 点击通讯录,进入通讯录页面
            2。 点击添加成员按钮,进入添加成员页面
            3。 点击手动输入添加按钮,输入成员信息页面
            5。 输入姓名,手机号,点击保存按钮
            6。 返回添加成员页面
            # 点击通讯录按钮
            self.driver.find_element(
                AppiumBy.XPATH,
                "//*[@text='通讯录']").click()
            # 滑动点击添加成员按钮
            self.swipe_find("添加成员").click()
            # 点击手动输入添加按钮
            self.driver.find_element(
                AppiumBy.XPATH,
                "//*[@text='手动输入添加']").click()
            # 定位姓名输入框
            self.driver.find_element(
                AppiumBy.XPATH,
                "//*[contains(@text, '姓名')]/../*[@text='必填']").send_keys(self.name)
            # 定位手机号输入框
            self.driver.find_element(
                AppiumBy.XPATH,
                "//*[contains(@text, '手机')]/..//*[@text='必填']").send_keys(self.phonenum)
            # 点击保存按钮
            self.driver.find_element(
                AppiumBy.XPATH,
                "//*[@text='保存']").click()
            tips = self.driver.find_element(
                AppiumBy.XPATH,
                "//*[@class='android.widget.Toast']").text
            assert tips == "添加成功"
        def swipe_window(self):
            # 滑动操作
            # 获取设备的尺寸
            size = self.driver.get_window_size()
            # {"width": xx, "height": xx}
            print(f"设备尺寸为 {size}")
            width = size.get("width")
            height = size.get('height')
            # # 获取滑动操作的坐标值
            start_x = width / 2
            start_y = height * 0.8
            end_x = start_x
            end_y = height * 0.2
            # swipe(起始x坐标,起始y坐标,结束x坐标,结束y坐标,滑动时间(单位毫秒))
            self.driver.swipe(start_x, start_y, end_x, end_y, 2000)
        def swipe_find(self, text, max_num=5):
            通过文本来查找元素,如果没有找到元素,就滑动,
            如果找到了,就返回元素
            # 为了滑动操作更快速,不用等待隐式等待设置的时间
            self.driver.implicitly_wait(1)
            for num in range(max_num):
                    # 正常通过文本查找元素
                    ele = self.driver.find_element(AppiumBy.XPATH, f"//*[@text='{text}']")
                    print("找到元素")
                    # 能找到则把隐式等待恢复原来的时间
                    self.driver.implicitly_wait(15)
                    # 返回找到的元素对象
                    return ele
                except Exception:
                    # 当查找元素发生异常时
                    print(f"没有找到元素,开始滑动")
                    print(f"滑动第{num + 1}次")
                    # 滑动操作
                    self.swipe_window()
            # 把隐式等待恢复原来的时间
            self.driver.implicitly_wait(15)
            # 抛出找不到元素的异常
            raise NoSuchElementException(f"滑动之后,未找到 {text} 元素")
    
    Java 实现
    public class TestMember { public static AndroidDriver driver; @BeforeAll public static void setUpClass() { //配置信息 UiAutomator2Options uiAutomator2Options = new UiAutomator2Options() .setPlatformName("Android") .setAutomationName("uiautomator2") .setNoReset(true) .amend("appium:appPackage", "com.tencent.wework") .amend("appium:appActivity", ".launch.LaunchSplashActivity") .amend("appium:forceAppLaunch", true) .amend("appium:shouldTerminateApp", true); //初始化 try { driver = new AndroidDriver(new URL("http://127.0.0.1:4723"), uiAutomator2Options); driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(5)); } catch (MalformedURLException e) { throw new RuntimeException(e); @AfterAll public static void tearDownClass(){ driver.quit(); @ParameterizedTest @CsvSource({"李四, 13188890000"}) public void addMember(String name, String phone) throws InterruptedException { driver.findElement(AppiumBy.xpath("//*[@text='通讯录']")).click(); for(int i =1; i<10; i++) { try { driver.findElement(AppiumBy.xpath("//*[@text='添加成员']")).click(); break; }catch (Exception e) { scrollDown(); driver.findElement(AppiumBy.xpath("//*[@text='手动输入添加']")).click(); driver.findElement(AppiumBy.xpath("//*[@text='姓名']//../*[@class='android.widget.EditText']")).sendKeys(name); driver.findElement(AppiumBy.xpath("//*[@text='手机']/..//*[@class='android.widget.EditText']")).sendKeys(phone); driver.findElement(AppiumBy.xpath("//*[@text='保存']")).click(); String toastMessage = driver.findElement(AppiumBy.xpath("//*[@class='android.widget.Toast']")).getText(); driver.navigate().back(); driver.findElement(AppiumBy.xpath("//*[@text='Hufflepuff']/../../../../*[3]/*[1]")).click(); driver.findElement(AppiumBy.xpath("//*[@text='搜索']")).sendKeys(name); List<WebElement> webElements = driver.findElements(AppiumBy.xpath("//*[@class='androidx.recyclerview.widget.RecyclerView']//*[@class='android.widget.LinearLayout']//*[@class='android.widget.TextView']")); // 1. 遍历所有元素 2. 获取所有元素对应的文本 3. 判断是否包含张三 4. 返回判断结果,如果所有元素都包含,则返回true assertTrue(webElements.stream().allMatch(item -> item.getText().contains("李四"))); assertEquals("添加成功", toastMessage); public static void scrollDown() { // 获取屏幕高度和宽度 int height = driver.manage().window().getSize().getHeight(); int width = driver.manage().window().getSize().getWidth(); // 设置滑动起始和结束坐标 int startY = (int) (height * 0.8); int startX = width / 2; int endY = (int) (height * 0.2); TouchAction touchAction = new TouchAction(driver); touchAction.longPress(ElementOption.point(startX, startY)) .moveTo(ElementOption.point(startX, endY)) .release() .perform();

    实战 3:用例优化

  • 用例执行过程中添加截图。
  • 将截图保存到 allure 报告中。
  • Python 实现
    import os
    import time
    import allure
    from appium import webdriver
    from appium.webdriver.common.appiumby import AppiumBy
    from appium.options.common import AppiumOptions
    root_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    @allure.feature("企业微信联系人操作")
    class TestWithShot:
        def setup(self):
            # Capability 设置定义为字典
            caps = {}
            # 设置 app 安装的平台(Android、iOS)
            caps["platformName"] = "Android"
            # 设置 app 安装平台的版本
            caps["appium:platformVersion"] = "6"
            # 设备的名字
            caps["appium:deviceName"] = "MuMu"
            # 设置 app 的包名
            caps["appium:appPackage"] = "com.tencent.wework"
            # 设置 app 启动页
            caps["appium:appActivity"] = ".launch.LaunchSplashActivity"
            # 不清空缓存
            caps["appium:noReset"] = True
            # app 不重启
            # caps["appium:dontStopAppOnReset"] = True
            # 实例化 AppiumOptions 对象
            options = AppiumOptions().load_capabilities(caps)
            # 初始化 driver
            self.driver = webdriver.Remote("http://127.0.0.1:4723/wd/hub", options=options)
            # 设置全局的隐式等待
            self.driver.implicitly_wait(10)
        def screenshot(self):
            :param path: 截图保存路径
            cur_time = time.strftime("%Y-%m-%d-%H-%M-%S")
            file_path = cur_time + ".png"
            dir_path = os.sep.join([root_path, "screenshot"])
            # 资源目录如果不存在则新创建一个
            if not os.path.isdir(dir_path):
                os.mkdir(dir_path)
            # 截图保存路径
            source_path = os.sep.join([dir_path, file_path])
            self.driver.save_screenshot(source_path)
            # 返回保存图片的路径
            return source_path
        @allure.story("搜索联系人")
        def test_search_contact(self):
            冒烟用例,搜索存在的联系人
            search_key = "蚊子"
            # 点击通讯录按钮
            with allure.step("点击通讯录按钮"):
                image_path = self.screenshot()
                self.driver.find_element(
                    AppiumBy.XPATH,
                    "//*[@text='通讯录']").click()
                allure.attach.file(image_path, name="点击通讯录按钮", attachment_type=allure.attachment_type.PNG)
            # 点击搜索按钮
            with allure.step("点击搜索按钮"):
                image_path = self.screenshot()
                self.driver.find_element(
                    AppiumBy.XPATH,
                    '//*[@text="悠然科技"]/../../../'
                    'following-sibling::*/*[1]').click()
                allure.attach.file(image_path, name="点击搜索按钮", attachment_type=allure.attachment_type.PNG)
            # 查找搜索框,输入搜索关键词
            with allure.step("输入搜索关键词"):
                self.driver.find_element(
                    AppiumBy.XPATH,
                    "//*[@text='搜索']").send_keys(search_key)
                image_path = self.screenshot()
                allure.attach.file(image_path, name="输入搜索关键词", attachment_type=allure.attachment_type.PNG)
            # 获取搜索结果元素的文本,完成断言
            with allure.step("获取搜索结果元素的文本,完成断言"):
                eles = self.driver.find_elements(
                    AppiumBy.XPATH,
                    '//*[@class="android.widget.ListView"]//*[@class="android.widget.ImageView"]/../../following-sibling::*/*[1]/*[1]')
                image_path = self.screenshot()
                allure.attach.file(image_path, name="获取搜索结果元素的文本,完成断言",
                              attachment_type=allure.attachment_type.PNG)
                assert search_key in [ele.text for ele in eles]
    
    Java 实现
    import io.appium.java_client.AppiumBy;
    import io.appium.java_client.TouchAction;
    import io.appium.java_client.android.AndroidDriver;
    import io.appium.java_client.android.options.UiAutomator2Options;
    import io.appium.java_client.touch.offset.ElementOption;
    import io.qameta.allure.Allure;
    import org.junit.jupiter.api.AfterAll;
    import org.junit.jupiter.api.BeforeAll;
    import org.junit.jupiter.api.Test;
    import org.junit.jupiter.params.ParameterizedTest;
    import org.junit.jupiter.params.provider.CsvSource;
    import org.openqa.selenium.OutputType;
    import org.openqa.selenium.TakesScreenshot;
    import org.openqa.selenium.WebElement;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.IOException;
    import java.net.MalformedURLException;
    import java.net.URL;
    import java.time.Duration;
    import java.util.List;
    import static org.junit.jupiter.api.Assertions.assertEquals;
    import static org.junit.jupiter.api.Assertions.assertTrue;
    public class TestMember {
        public static AndroidDriver driver;
        @BeforeAll
        public static void setUpClass() {
            //配置信息
            UiAutomator2Options uiAutomator2Options = new UiAutomator2Options()
                    .setPlatformName("Android")
                    .setAutomationName("uiautomator2")
                    .setNoReset(true)
                    .amend("appium:appPackage", "com.tencent.wework")
                    .amend("appium:appActivity", ".launch.LaunchSplashActivity")
                    .amend("appium:forceAppLaunch", true)
                    .amend("appium:shouldTerminateApp", true);
            //初始化
            try {
                driver = new AndroidDriver(new URL("http://127.0.0.1:4723"), uiAutomator2Options);
                driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(5));
            } catch (MalformedURLException e) {
                throw new RuntimeException(e);
        @AfterAll
        public static void tearDownClass(){
            driver.quit();
        private void ElementScreenBase(String message) throws IOException {
            File screenshot = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
            Allure.addAttachment(message, "image/png", new FileInputStream(screenshot), ".png");
         * 前置条件: 登录成功
         * 1. 进入通讯录页面
         * 2. 点击搜索按钮,输入一个已经存在的成员名称。
        @Test
        public void searchMember() throws IOException {
            driver.findElement(AppiumBy.xpath("//*[@text='通讯录']")).click();
            driver.findElement(AppiumBy.xpath("//*[@text='Hufflepuff']/../../../../*[3]/*[1]")).click();
            driver.findElement(AppiumBy.xpath("//*[@text='搜索']")).sendKeys("张三");
            List<WebElement> webElements =  driver.findElements(AppiumBy.xpath("//*[@class='androidx.recyclerview.widget.RecyclerView']//*[@class='android.widget.LinearLayout']//*[@class='android.widget.TextView']"));
            //  1. 遍历所有元素 2. 获取所有元素对应的文本 3. 判断是否包含张三 4. 返回判断结果,如果所有元素都包含,则返回true
            // 到最后一个页面进行截图
            ElementScreenBase("查找成员");
            assertTrue(webElements.stream().allMatch(item -> item.getText().contains("张三")));
    
  • 编写添加成员成功和失败的测试用例,具体参考测试用例-添加联系人测试场景。
  • 测试结果需添加截图到报告中。
  • 观察 Appium Server/Desktop 的日志信息,了解 Appium 相关的通信原理。
  • 环境安装与配置
  • Appium 环境安装

  • emulator 安装应用方法
  • 模拟器控制