App自动化测试框架appium+python+appnium_python_client及实现

“原创,并未实现数据驱动,所以此框架仅供参考,前提需要查看者熟悉unittest、selenium的使用方法,和良好的python代码编程技术”

实现材料:python、appium服务器、selenium、夜神模拟器、哔哩哔哩手机客户端

目标:通过自动化脚本实现哔哩哔哩app端搜索肯德基,进入视频,并判断是否进入成功

1.Appium配置

运行appium需要sdk和jdk的支持,安装方法网上有很多,在此不赘述

系统变量ANDROID_HOME:sdk所在的目录

系统变量JAVA_HOME:jdk所在目录

(路径仅供参考,需要联合实际目录)

点击startserver启动服务,默认开在本机的4723端口(127.0.0.1:4723)

 

可以通过访问http://127.0.0.1:4723/wd/hub验证服务是否开启成功 (此为成功开启)

 为方便调试,可以把adb的所在目录添加进系统变量,方便命令提示符的调用

启动模拟器,输入adb命令查看是否连接成功

2.通过POM设计自动化测试脚本

所谓POM可称为页面对象模式(page object model),主旨是将动作、数据、程序相分离

动作和程序的关系相当于:动作是跑、跳、看、卧倒等基础动作,但你如果指挥一个人去卧室躺在床上,光靠单个动作可完成不了,须要由动作组成的程序来完成

所以需要计划创建三个python包裹,分别是页面基类包、动作程序包、测试用例包

首先创建页面基类包,并在其中创建py文件appbasic

这个包里面包括对象实例、基本动作

代码主体:

#系统包
import os
#appium模块
from appium import webdriver
from appium.webdriver.common.appiumby import By
#selenium模块,用于元素判断和元素等待
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait
#时间包,用于等待
import time


'''
页面基类driver和一些常用的动作方法
'''

#定义一个driver类
class driver:
    # 初始化驱动
    def __init__(self, device, msg):
        self.caps = msg
        #appium对象驱动是依靠webdriver.Remote()方法建造的,需要传入appium服务地址和连接信息
        self.dv = webdriver.Remote(device, self.caps)

    # 等待某个元素
    def element_wait(self, ele):
        #这里用try是防止人为误判,如果没有该元素会报错,所以保底让他停留1秒
        try:
            '''
WebDriverWait方法需要传入驱动对象和最大等待时间,
EC(这里是EC因为在导包的时候使用了EC作为别名,所以直接引用),
EC.presence_of_element_located((By.XPATH, f'{ele}')此方法是判断该元素是否存在
,结合until方法会持续判断,判断到10秒钟结束。如果存在则提前结束等待
'''
            WebDriverWait(self.dv, 10).until(EC.presence_of_element_located((By.XPATH, f'{ele}')))
        except:
            #如果没有这个元素则强制等待1秒
            time.sleep(1)

    # 点击某个元素
    def element_click(self, ele):
        #通过提前等待,让点击动作更智能,降低报错率,因为有时候程序执行会快过页面加载
        self.element_wait(ele)
        #元素点击的动作
        self.dv.find_element(By.XPATH, f"{ele}").click()

    # 往指定元素输入值
    def element_sendkey(self, ele, value):
        self.element_wait(ele)
        #元素输入值的动作
        self.dv.find_element(By.XPATH, f"{ele}").send_keys(value)

    # 按键事件,需输入按键对应值
    def element_event(self, num):
        '''通过调用命令提示符(cmd)调用adb命令,来执行手机的按键操作
(任务栏、home键等,是用数字代替的,具体动作对应的数字可以百度查询)'''
        os.system(f"adb shell input keyevent {num}")

    # 获得所有指定元素内的值
    def element_gettexts(self, ele):
        self.element_wait(ele)
        #通过一个循环将符合该元素特征的元素值添加到一个列表里,并返回该列表
        text = []
        for i in self.dv.find_elements(By.XPATH, f'{ele}'):
            text.append(i.text)
        return text

    # 输出页面源码
    def backpagesource(self):
        #self.dv.page_source方法用于查看页面源代码
        return self.dv.page_source

    # 通过元素的text属性值判断元素是否存在
    def is_element_exist(self, ele):
        #这里用上面定义的等待方法初筛,没等到则返回FALSE
        try:
            self.element_wait(f"//*[@text='{ele}']")
        except:
            return False
        #二次筛选,判断该元素的text属性是否存在于源码中
        if ele in self.dv.page_source:
            return True
        else:
            return False
    #关闭程序
    def close_package(self,packagename):
        try:
            self.dv.terminate_app(packagename)
        except:
            pass

 第二步创建页面程序包,包括具体的动作,并创建py文件findKFC_bili

 

 代码主体:

#导入页面基类模块和driver基类
from base.appbasic import driver
import time
'''
页面动作类,将基类方法组成动作程序
'''

#定义action类,并继承driver基类
class action(driver):
    # 往输入框输入值
    #别担心为什么没有传入驱动,因为action继承了driver基类,可以通过实例action类来创建驱动
    def Search_Word(self, str):
        #点击按钮的元素属性
        clickele = "//*[@resource-id='tv.danmaku.bili:id/expand_search']"
        #等待元素的属性
        waitele = "//*[@content-desc='搜索查询']"
        #查找框的属性
        searchele = "//*[@content-desc='搜索查询']"
        #视频标题的元素
        videonameele = "//*[@class='android.widget.TextView'][@resource-id='tv.danmaku.bili:id/title']"
        #将元素属性传入动作方法中
        self.element_wait(clickele)
        self.element_click(clickele)
        self.element_sendkey(searchele, str)
        self.element_wait(waitele)
        self.element_event("KEYCODE_ENTER")
        self.element_wait(videonameele)
        #查询动作结束

    # 进入到第X个视频里,执行该方法的前提是搜索动作执行完毕且无误
    def Into_Video(self, ele, str):
        self.element_wait(ele)
        #获得视频标题列表
        self.midbox = self.element_gettexts(ele)
        #点击第X个视频
        self.element_click(f"//*[@text='{self.midbox[str]}']")
        #返回视频列表用于断言
        return self.midbox

    # 关闭弹窗
    #通过输入弹窗标题关闭
    def alertclose(self, txt):
        time.sleep(1.5)
        #backpagesource()方法用于返回页面源码
        source = self.backpagesource()
        #如果弹窗元素存在于源码中,按两次返回键
        if txt in source:
            self.element_event(4)
            self.element_event(4)

 第三步创建测试用例包,并创建py文件testcas

代码主体:

#导入unittest包
import unittest
#导入页面动作包和action类
from page.findKFC_bili import action
#省略警告信息的包
import warnings


# 测试用例类,继承unittest.TestCase类
class bilicase(unittest.TestCase):
    #每一个测试用例开始前都会调用该方法,所以将驱动信息放于此,并实例驱动
    def setUp(self):
        #忽略警告信息
        warnings.simplefilter("ignore", ResourceWarning)
        warnings.simplefilter("ignore", DeprecationWarning)
        #驱动信息
        self.caps = {}
        #模拟器的地址和端口,通过cmd命令adb devices可查看
        self.caps["deviceName"] = "127.0.0.1:62001"
        #模拟器的型号
        self.caps["platformName"] = "Android"
        #模拟器版本,通过手机设置查看
        self.caps["platformVersion"] = "7.1"
        #运行程序的包名,桌面程序的程序包名,通过点击桌面上的哔哩哔哩app来进入软件
        #直接用unittest打开哔哩哔哩是一个初始化的程序,需要等待很久很久
        #所以直接通过桌面点击的方法来跳过初始化
        self.caps["appPackage"] = "com.android.launcher3"
        #活动窗口名,运行程序包的哪一个窗口
        self.caps["appActivity"] = ".launcher3.Launcher"
        #appium服务器的地址
        appiumlocate = "http://127.0.0.1:4723/wd/hub"
        #实例驱动对象
        self.obj = action(appiumlocate, self.caps)
    
    #每次测试结束都会运行该方法
    def tearDown(self):
        #关闭哔哩哔哩app
        self.obj.close_package('tv.danmaku.bili')
    #测试方法,以test_开头
    def test_findcase(self):
        #点击桌面的哔哩哔哩客户端
        self.obj.element_click("//*[@text='哔哩哔哩']")
        #关闭登录弹窗
        self.obj.alertclose('登录注册解锁更多精彩内容')
        #查询肯德基的信息
        self.obj.Search_Word("肯德基")
        #进入第X个视频
        num = 0
        #通过调用Into_Video方法,既进入了视频,又返回了一个视频列表
        box = self.obj.Into_Video(
            "//*[@resource-id='tv.danmaku.bili:id/title'][@class='android.widget.TextView'][@index='1']", num)
        #通过is_element_exist()方法判断元素是否存在,并断言
        self.assertEqual(True, self.obj.is_element_exist(box[num]))

3.通过一个控制面板生成测试报告

代码体

#导入html测试报告包,此包为第三方作者所编写
#下载请百度
import HTMLTestRunner
import unittest
#导入用例
from cases.testcas import bilicase

#创建一个加载器,并通过loadTestsFromTestCase()方法载入测试类bilicase
suite = unittest.TestLoader().loadTestsFromTestCase(bilicase)
#创建文件流,报告将存放在桌面
fp = open(r'C:\Users\Administrator\Desktop\baogao.html', 'wb')
#创建执行器,标题为:哔哩哔哩搜索功能测试报告
runner = HTMLTestRunner.HTMLTestRunner(fp,title='哔哩哔哩搜索功能测试报告')
#执行加载器
runner.run(suite)

执行过程

HTML报告: