20220814 app自动化训练营

PPT

代码地址

课后调查表

https://jinshuju.net/f/Xdpzjw

连接设备

mac: adb kill-server&&adbdevices
window:adb connect 127.0.0.1:7555

获取包名和页面名

mac/linux : adb logcat ActivityManager:I | grep "cmp"
windows: adb logcat ActivityManager:I | findstr "cmp"

  • 第一步:先在手机上关闭应用程序(被测应用程序)
  • 第二步:执行上面的命令
  • 第三步:启动被测应用程序

添加成员

"""
__author__ = '霍格沃兹测试开发学社'
__desc__ = '更多测试开发技术探讨,请访问:https://ceshiren.com/t/topic/15860'
"""
# This sample code uses the Appium python client
# pip install Appium-Python-Client
# Then you can paste this into a file and simply run with Python
# pip install appium-python-client
from time import sleep

from appium import webdriver
from appium.webdriver.common.appiumby import AppiumBy
from faker import Faker
from selenium.common import NoSuchElementException
from selenium.webdriver.support.wait import WebDriverWait


class TestContact:
    _IMPLICITLY = 20
    def setup_class(self):
        self.faker = Faker('zh_CN')

    def setup(self):
        # 准备工作
        # 字典对象 desirecapbility
        caps = {}
        caps["platformName"] = "android"
        caps["deviceName"] = "emulator-5554"
        caps["appPackage"] = "com.tencent.wework"
        caps["appActivity"] = ".launch.LaunchSplashActivity"
        # 防止 每次启动都关闭app 再重新启动,这个参数要与noReset 结合使用才会生效
        caps["dontStopAppOnReset"] = "true"
        # 设置运行过一次之后,就可以跳过服务的安装,跳过设备的初始化,提升启动速度
        caps["skipDeviceInitialization"] = "true"
        caps["skipServerInstallation"] = "true"
        caps["noReset"] = "true"
        # 最重要的步骤!! 让我们的客户端与appium server 服务端建立 连接最关键的一步
        # 服务端会返回一个session对象,存放了要测试的设备信息
        # 与server 建立连接的时候 ,默认打开 desirecaps里配置的启动页面
        self.driver = webdriver.Remote("http://127.0.0.1:4723/wd/hub", caps)
        self.driver.implicitly_wait(self._IMPLICITLY)

    def set_implicit(self,waittime):
        self.driver.implicitly_wait(waittime)

    def swipe_find(self, text, max_num = 3):
        """滑动查找"""
        # max_num = 3
        self.set_implicit(1)
        for i in range(max_num):
            try:
                # 每次找元素都要等足 _IMPLICITLY 隐式等待时长
                element = self.driver.find_element(AppiumBy.XPATH, f"//*[@text='{text}']")
                self.set_implicit(self._IMPLICITLY)
                return element
            except NoSuchElementException:
                # 滑动
                size = self.driver.get_window_size()
                # 'width', 'height'
                startx = size.get("width")/2
                starty = size.get("height") * 0.8

                endx = startx
                endy = size.get("height") * 0.3
                duration = 500
                self.driver.swipe(startx,starty,endx,endy,duration)
            if i == max_num-1:
                # 找了最大次数 ,仍然未找到
                self.set_implicit(self._IMPLICITLY)
                raise NoSuchElementException(f"找了 {max_num} 次,未找到 {text}")


    def test_addcontact(self):
        """
        添加联系人
        """
        name = self.faker.name()
        phonenumber = self.faker.phone_number()
        # 进入【通讯录】页面
        self.driver.find_element(AppiumBy.XPATH, "//*[@text='通讯录']").click()
        # 点击【添加成员】
        # self.driver.find_element(AppiumBy.XPATH, "//*[@text='添加成员']").click()
        self.swipe_find("添加成员", 3).click()
        # 点击【手动输入添加】
        self.driver.find_element(AppiumBy.XPATH, "//*[@text='手动输入添加']").click()
        # 输入【姓名】【手机号】并点击【保存】
        # 找到第一次出现的元素 就返回
        # self.driver.find_element(AppiumBy.XPATH, "//*[@text='必填']").send_keys(name)
        self.driver.find_element(AppiumBy.XPATH, "//*[contains(@text,'姓名')]/../*[@text='必填']").send_keys(name)
        # 第一种方式通过 查找它们公共的父结点->找子孙结点里的EditText
        self.driver.find_element(AppiumBy.XPATH, "//*[contains(@text,'手机')]/..//android.widget.EditText").send_keys(
            phonenumber)
        # 第二种:通过查找公共的父结点->找子孙结点里文字为[必填]的元素
        # //*[contains(@text,'手机')]/..//*[@text='必填']
        self.driver.find_element(AppiumBy.XPATH, "//*[@text='保存']").click()
        # 验证点:登录成功提示信息
        # sleep(2)
        # print(self.driver.page_source)
        # 等待 toast 出现, 默认使用隐式等待,
        toast = self.driver.find_element(AppiumBy.XPATH, "//*[@class='android.widget.Toast']").text
        assert "添加成功" == toast
        # 纯显式等待查找
        # WebDriverWait(self.driver, 10).until(lambda x:"添加成员" in x.page_source)
        # 显式等待+隐式等待
        # WebDriverWait(self.driver,10).until(lambda x:x.find_element(AppiumBy.XPATH,"//*[@class='android.widget.Toast']"))


    def teardown(self):
        # 资源回收
        self.driver.quit()


作业

  • 模拟器->右下角更多-》 定位-》点设置

  • 实现外出打卡
  • 前提条件:
    • 1、提前注册企业微信管理员帐号
    • 2、手机端安装企业微信
    • 3、企业微信 app 处于登录状态
  • 实现打卡功能
    • 打开【企业微信】应用
    • 进入【工作台】页面
    • 点击【打卡】
    • 选择【外出打卡】tab
    • 点击【第 N 次打卡】
  • 验证点:提示【外出打卡成功】
# author: 一路向北
# project:pythonProject
# name: test_clock_in.py
# date: 2022/8/14
"""
实现打卡功能
打开【企业微信】应用
进入【工作台】页面
点击【打卡】
选择【外出打卡】tab
点击【第 N 次打卡】
验证点:提示【外出打卡成功】
"""

from appium import webdriver
from appium.webdriver.common.mobileby import MobileBy
from selenium.common.exceptions import NoSuchElementException
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.support.wait import WebDriverWait


class TestClockIn:
    # 隐式等待时长
    __IMPLICITLY = 5

    def implicitly_set(self, wait_time):
        self.driver.implicitly_wait(wait_time)

    def find_click(self, by, locator):
        # 正常查找并点击
        # self.driver.find_element(by,locator).click()
        # 加上显示等待,等元素可以点击后再点击
        WebDriverWait(self.driver, 5).until(expected_conditions.element_to_be_clickable((by, locator))).click()

    def swipe_find(self):
        self.implicitly_set(1)
        # 最大查找次数
        max_try_num = 3
        for i in range(max_try_num):
            # 尝试定位目标元素
            try:
                element = self.driver.find_element(MobileBy.XPATH, "//*[@text='打卡']")
                self.implicitly_set(self.__IMPLICITLY)
                return element
            # 未找到目标元素,滑动页面
            except NoSuchElementException:
                # 将【工作台】页面往下滑动,以便可以看到【打卡】选项
                window_size = self.driver.get_window_size()
                x1 = window_size['width'] / 2
                y_start = window_size['height'] * 4 / 5
                y_end = window_size['height'] * 1 / 5
                duration = 500
                self.driver.swipe(x1, y_start, x1, y_end, duration)
                # 找了最大次数后仍未找到,抛出异常
                if i == max_try_num - 1:
                    self.implicitly_set(self.__IMPLICITLY)
                    raise NoSuchElementException(f"滑动了{max_try_num}次,仍未找到目标元素")

    def setup(self):
        # 准备工作
        # 打开【企业微信】应用
        caps = {}
        caps["platformName"] = "android"
        caps["deviceName"] = "emulator-5554"
        caps["appPackage"] = "com.tencent.wework"
        caps["appActivity"] = ".launch.LaunchSplashActivity"
        # 防止 每次启动都关闭app 再重新启动,这个参数要与noReset 结合使用才会生效
        caps["dontStopAppOnReset"] = "true"
        caps["noReset"] = "true"
        # 与server 建立连接的时候 ,默认打开 desirecaps里配置的启动页面
        self.driver = webdriver.Remote("http://127.0.0.1:4723/wd/hub", caps)
        self.implicitly_set(self.__IMPLICITLY)

    def test_clock_in(self):
        """
        添加联系人
        """
        # 进入【工作台】页面
        self.find_click(MobileBy.XPATH, "//*[@text='工作台']")
        # 点击【打卡】
        self.swipe_find().click()
        # 选择【外出打卡】tab
        self.find_click(MobileBy.ID, "com.tencent.wework:id/jt8")
        # 点击【第 N 次打卡】
        self.find_click(MobileBy.XPATH, "//*[contains(@text,'次外出')]")
        # 验证点:提示【外出打卡成功】
        toast = self.driver.find_element(MobileBy.ID, "com.tencent.wework:id/rr").text
        assert "外出打卡成功" == toast

    def teardown(self):
        # 资源回收
        self.driver.quit()
from appium import webdriver
from appium.webdriver.common.appiumby import AppiumBy
from faker import Faker
from selenium.common import NoSuchElementException
from selenium.webdriver.support.wait import WebDriverWait


class TestAppiumWework:
    _IMPLICITLY = 8
    def setup(self):
        cap = {}
        cap["platformName"] = "Android"
        cap["appPackage"] = "com.tencent.wework"
        cap["appActivity"] = ".launch.LaunchSplashActivity"
        cap["deviceName"] = "WeworkDevice"
        # 设置动态页面的等待时长
        cap['settings[waitForIdleTimeout]'] = 0
        cap["noReset"] = True
        # 防止 每次启动都关闭app 再重新启动,这个参数要与noReset 结合使用才会生效
        cap["dontStopAppOnReset"] = True
        # 设置运行过一次之后,就可以跳过服务的安装,跳过设备的初始化,提升启动速度
        cap["skipDeviceInitialization"] = True
        cap["skipServerInstallation"] = True
        self.driver = webdriver.Remote("http://127.0.0.1:4723/wd/hub", cap)
        self.set_implicitly(self._IMPLICITLY)

    def teardown(self):
        self.driver.quit()

    def set_implicitly(self, waittime):
        self.driver.implicitly_wait(waittime)

    def test_addcontent(self):
        '''
        添加联系人
        :return:
        '''
        # 进入[通讯录]页面
        self.driver.find_element(AppiumBy.XPATH, "//*[@text='通讯录']").click()
        # 点击[添加成员]
        self.swipe_find("添加成员").click()
        # 点击[手动输入添加]
        self.driver.find_element(AppiumBy.XPATH, "//android.widget.TextView[contains(@text, '手动输入添加')]").click()
        # 输入[姓名][手机号]并点击[保存]
        faker1 = Faker("zh_Cn")
        username = faker1.name()
        mobile = faker1.phone_number()
        self.driver.find_element(AppiumBy.XPATH, "//android.widget.TextView[contains(@text, '姓名')]"
                                                 "/following-sibling::android.widget.EditText[1]").send_keys(username)
        self.driver.find_element(AppiumBy.XPATH, "//android.widget.TextView[contains(@text, '手机')]//"
                                                 "following-sibling::android.widget.RelativeLayout[1]//"
                                                 "android.widget.RelativeLayout//"
                                                 "android.widget.EditText[@text='必填']").send_keys(mobile)
        # "//android.widget.TextView[contains(@text, '手机')]/..//android.widget.EditText[contains(@text, '必填')]"
        self.driver.find_element(AppiumBy.XPATH, "//*[@text='保存']").click()
        # sleep(2)
        # print(self.driver.page_source)
        # 找到第一次出现的元素就返回
        # 第一种方式通过查找他们公共的父节点-->找子孙节点的EditText
        # 第二种:通过查找公共的父结点->找子孙结点里文字为[必填]的元素
        # 验证点:登录成功提示信息
        # 等待 toast 出现, 默认使用隐式等待,调用find_element会触发隐式等待
        toast = self.driver.find_element(AppiumBy.XPATH, "//*[@class='android.widget.Toast']").text
        # 纯显示等待
        # WebDriverWait(self.driver, 10).until(lambda x: "添加成员" in x.page_source)
        # 显示等待+隐式等待
        # WebDriverWait(self.driver, 10).until(lambda x: x.find_element(AppiumBy.XPATH, "android.widget.Toast"))
        assert "添加成功" == toast
        # 纯显式等待查找
        # 显式等待+隐式等待

    def test_punch_out(self):
        # 进入[工作台]页面
        self.driver.find_element(AppiumBy.XPATH, "//*[@text='工作台']").click()
        # 点击[打卡]
        self.swipe_find("打卡").click()
        self.driver.find_element(AppiumBy.XPATH, "//*[@text='完成']").click()
        self.swipe_find("打卡").click()
        # 选择[外出打卡]tab
        self.driver.find_element(AppiumBy.XPATH, "//*[contains(@text, '外出打卡')]").click()
        # 点击[第N次打卡]
        self.driver.find_element(AppiumBy.XPATH, "//*[contains(@text, '次外出')]").click()

        # self.driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR, new UIScrollable scrollintoview)
        result = self.driver.find_element(AppiumBy.XPATH, "//*[@text='外出打卡成功']").text
        assert "外出打卡成功" == result

    # 通过swipe向上滑动直到页面能看到"添加成员"按钮
    def swipe_find(self, text):
        size = self.driver.get_window_size()
        width = size.get("width")
        height = size.get("height")
        start_x = width / 2
        start_y = height * 0.8
        end_x = width / 2
        end_y = height * 0.3
        duration = 500
        # 循环查找"添加成员"按钮
        nums = 4
        self.set_implicitly(1)
        for i in range(nums):
            try:
                ele = self.driver.find_element(AppiumBy.XPATH, f"//*[@text='{text}']")
                self.set_implicitly(self._IMPLICITLY)
                return ele
            except NoSuchElementException:
                self.driver.swipe(start_x, start_y, end_x, end_y, duration)
            if i == nums - 1:
                self.set_implicitly(self._IMPLICITLY)
                raise NoSuchElementException(f"找了{nums}次,未找到{text}")
![01|800x249](upload://2DlHcfCmfG7TbSC6GHB0gP4syoc.png)
![02|516x1019](upload://8grg6sYk8qwhZd4wGczQkqlEcVI.png)
from appium import webdriver
from appium.webdriver.common.mobileby import MobileBy
from faker import Faker
from selenium.common.exceptions import NoSuchElementException


class TestWechat:
    _IMPLICITLY = 20
    def setup_class(self):
        testfaker = Faker("zh_Cn")
        self.name = testfaker.name()
        self.phone = testfaker.phone_number()


    def setup(self):
        desired_caps = {}
        desired_caps['platformName'] = 'Android'
        desired_caps['platformVersion'] = '6.0'
        desired_caps['deviceName'] = '127.0.0.1:7555'
        desired_caps['appPackage'] = 'com.tencent.wework'
        desired_caps['appActivity'] = '.launch.LaunchSplashActivity'
        desired_caps['noReset'] = 'true'
        # 设置动态页面等待时间
        desired_caps['settings[waitForIdleTimeout]'] = 100
        # 不重启应用
        desired_caps['dontStopAppOnReset'] = 'true'
        # 跳过安装服务
        desired_caps['skipServerInstallation'] = True
        # 跳过设备的初始化
        desired_caps['skipDeviceInitialization'] = 'true'
        self.driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps)
        # 每次find_element的时候都会显示等待,一般来说,这种显示等待已经够了,所以不需要再去设置等待
        self.driver.implicitly_wait(self._IMPLICITLY)


    def teardown(self):
        self.driver.quit()


    # 切换等待时间方法,由于每次查找都会显示等待,所以滑动查找的时候,需要临时将等待时长缩短
    def set_implicit(self, waittime):
        self.driver.implicitly_wait(waittime)


    def swipe_find(self,text,max_num = 3):
        self.set_implicit(1)
        for i in range(max_num):
            try:
                element = self.driver.find_element(MobileBy.XPATH,f"//*[@text='{text}']")
                self.set_implicit(self._IMPLICITLY)
                return element
            except NoSuchElementException:
                # 获取当前手机尺寸
                size = self.driver.get_window_size()
                startx = size.get("width")/2
                starty = size.get("height")*0.8

                endx = startx
                endy = size.get("height")*0.3
                # swipe滑动操作,duration滑动时间
                self.driver.swipe(startx,starty,endx,endy,duration=500)
            if i == max_num-1:
                # 如果超过查找最大次数,抛出异常
                self.set_implicit(self._IMPLICITLY)
                raise NoSuchElementException(f"找了{max_num}次,没找到")


    def test_case(self):
        self.driver.find_element(MobileBy.XPATH,"//*[@text='通讯录']").click()
        # self.driver.find_element(MobileBy.XPATH,"//*[@text='添加成员']").click()
        self.swipe_find('添加成员').click()
        self.driver.find_element(MobileBy.XPATH,"//*[@text='手动输入添加']").click()
        # 由于可能text中包含空格,所以可以通过contains去定位包含某些文本的元素
        self.driver.find_element(MobileBy.XPATH,"//*[contains(@text,'姓名')]/../*[@text='必填']").send_keys(self.name)
        self.driver.find_element(MobileBy.XPATH,"//*[contains(@text,'手机')]/..//*[@text='必填']").send_keys(self.phone)
        self.driver.find_element(MobileBy.XPATH,"//*[@text='保存']").click()
        # time.sleep(4)
        # print(self.driver.page_source)
        toast = self.driver.find_element(MobileBy.XPATH, "//*[@class='android.widget.Toast']").text
        assert "添加成功" == toast

    def test_clock(self):
        self.driver.find_element(MobileBy.XPATH,"//*[@text='工作台']").click()
        self.swipe_find('打卡').click()
        self.driver.find_element(MobileBy.ID,"com.tencent.wework:id/jt8").click()
        n = self.driver.find_element(MobileBy.ID,"com.tencent.wework:id/bdt").text
        self.driver.find_element(MobileBy.XPATH,f"//*[@text='{n}']").click()
        result = self.driver.find_element(MobileBy.ID,"com.tencent.wework:id/rr").text
        # time.sleep(4)
        # print(self.driver.page_source)
        assert "外出打卡成功" == result