3.app自动化项目

app自动化项目

我们可以使用AirtestIDE工具进行脚本的调试 元素的定位等辅助功能
但是真正意义上的脚本 在   AirtestIDE 工具中实现还是比较麻烦
问题:
1. 如何使用pycharm实现airtest内容脚本
    解决方案: 如果是已经实现差不多的脚本 可以直接将脚本复制到pycharm
             如果是还未实现的脚本  我们可以直接在pycharm进行编写(AirtestIDE进行辅助)
    问题:直接复制脚本 脚本在pycharm中报红   在pycharm直接进行编写,如何进行编写
    原因:项目环境中没有安装 airtest框架 及poco框架
    解决方案: 安装下 airtest框架 及poco框架
        pip install airtest -i https://pypi.tuna.tsinghua.edu.cn/simple
        pip install pocoui -i https://pypi.tuna.tsinghua.edu.cn/simple
    注意点:
        在使用pycharm运行时 airtestIDE可以不打开(安装airtest第三方包的时候就自带adb.exe)
        但是要注意adb版本保持一致(电脑上各个地方的adb要一个版本  找准你用的版本对其他不是该版本的adb进行替换)
        安装好的airtest框架 adb位置: 安装包文件夹\airtest\core\android\static\adb\abd.exe 
新的问题
2.代码右击运行打印出很多红色信息
    原因:这些红色信息都是airtest的日志信息  
        我们查看的时候理论上只需要看error以上级别(报错)
    解决方案: 取消error以下级别的日志即可
    如何取消:
        代码正式运行前添加如下代码:
        import logging
        logger = logging.getLogger("airtest")
        logger.setLevel(logging.ERROR)
        # 日志级别由高到底 分别是 [DEBUG]、[INFO]、[WARNING] 和 [ERROR]及 [Critical]
新的问题
3.airtest报告怎么生成
    解决方案:直接 调用airtest提供的生成报告的方法   即可生成报告
    from airtest.report.report import simple_report
    simple_report(__file__, logpath=True)
新的问题
4.面向过程实现业务
    缺点 没有实现业务分离  如果后期脚本变多 代码冗余 关联性很强 调试定位 都存在很多的问题
    优点:对于新手很友好 从头到尾实现逻辑 对于 新手理解比较容易
    解决方案: 面向对象的思维实现
        类: 将具有共同特征或行为的一组对象 可以抽象定义成类
            类是创建对象的模板 
        我们可以把整个项目定义一个类  每个方法对应每一个case  后期那个case存在问题 找到对应的方法 
        也可以针对单独的方法单独执行调试
新的问题
5. 项目没有分层 写在一个文件中 涉及到图片 数据等资源引用 后期项目结构太乱 
    解决方案: 实现分层(构建目录结构) 构建配置
        建立多个文件夹 不同的文件夹存放不同类型的内容
        构建配置化 尽量代码中少使用绝对路径 等写死的内容  尽量提供一个配置文件 可以在配置文件进行修改
            相关知识点
                __file__  获取当前文件 所在机器的一个绝对路径(项目在其他人电脑上部署 那么得到的就是对于他电脑所在的路径)
                os.path.dirname(路径)  获取所传路径的上一级目录
                os.path.join(路径1,路径2)  会根据路径2(如果是相对路径就会拼接  如果是绝对路径直接采用)及所在机器自动拼接完整路径
新的问题:             
6. 某个case报错 代码依旧会停止  用例单独执行 不能自动收集自动执行 并且生成汇总报告
    解决方案: 结合单元测试框架
        单元测试框架作用:
            用例自动收集,自动执行
            异常容错
            报告生成
            固件
    实现: 项目结合unittest
        unittest 四大核心  
            TestCase   
            TestSuite
            TestRunner
            Fixture
        具体步骤 
            1.声明之前定义好的类为测试类(类名继承 unittest.TestCase)
              注意:测试类的执行顺序(按照ASCII顺序)  测试类内不能有__init__
            2.使用套件执行 并结合BeautifulReport生成一份优美的报告
7. 项目运行 产生了 log文件夹 及报告依旧输出在项目的同级目录下  (如何保存及输出到指定的路径)     
    airtest每次运行都会产生一个 log文件夹 及 log.html
    log文件夹 是用来收集case运行过程中的步骤 图片等相关数据 用于 后期在log.html上进行报告渲染显示
    如何产生?
        通过auto_setup()中logdir参数 可以指定log文件夹的地址
        通过simple_report(filepath, logpath=True, logfile=None, output=HTML_FILE) 生成airtest报告
            filepath 文件路径接收的是当前文件路径 __file__
            logpath  日志路径  生成报告你得告诉我用什么数据(填写日志的路径 他就会读取这个日志文件夹里的数据) 
                auto_setup()中logdir写什么(存放路径 )   你得logpath(取出路径)就是什么
            logfile 具体的日志文件名称
            output  报告的输出路径 可以通过这个参数自定义报告的名称及输出位置
    解决方案:
        修改 auto_setup()  logdir参数   logdir填指定的报告存储路径
        simple_report中的logpath及 output参数
            logpath等同于logdir(从哪里存储从哪里读取)
            output 填报告的输出路径
8.airtest报告只有一份 理论上 一个case一份airtest报告 然后还有一份汇总的目录报告 
    问题原因:你只执行了一次生成报告的方法
    解决方案: 每个case执行完都去生成一次自己的报告  
            相当于每个case执行后调用一次   simple_report() 然后输出自己的报告
    新的问题:虽然每个case都有自己的报告 但是后面执行的case会有前面case的操作步骤及数据
    问题原因:因为你所有的case运行的数据都存放在同一个文件夹下  后续的case生成报告也从该文件夹下读取
            所以自然而然的会携带之前case的历史数据
    解决方案:每个case在运行之前 先创建属于自己的日志文件夹 后期生成报告从 自己的日志文件夹 中读取数据 并生成报告
            所以我们可以尝试把初始化的操作(auto_setup)在每个case执行前实现一遍 然后修改他们的logdir变成自己指定的日志路径
            但是 auto_setup 不光是建立日志文件夹 还要建立连接  代码存在冗余且 不合理
            我们的需求: 只要每个case执行前构建自己的日志文件夹即可 不需要夹杂其他的操作
            复制auto_setup的源码 将原有的函数 拆分成两个 
            only_auto_setup  专门用来建立连接
            及setlogdir       每个case运行前专门用它生成日志文件夹
9.虽然使用了单元测试框架 每个case报错 不会影响其他case执行  但每个case依旧会有关联
    问题原因: 手机端不像pc端 可以通过url定位到一个页面展开具体操作
            手机端一个case执行完 要么人为回到首页 开展下一个case 要么每次执行完关闭 重新打开在首页开始操作
            我们现在是人为回到首页  其他的操作都是基于首页面开展 然而某个case报错 他没有回到首页面 导致后面其他case无法开展
    解决方案: 无论是否成功 最终 每个case执行完成后都要执行返回到首页面的操作
    实现: 针对我们的业务步骤 进行异常捕获  无论业务是否失败 或者错误 最终都要执行返回操作
        使用 异常捕获机制 无论代码是否出现异常  最终都要执行返回
    新的问题: 执行返回的次数是动态的  不能写死
    解决方案: 我们本质只是希望case执行完之后能够 回到开始页面 进行下一个case的操作 
            只要不在开始页面我们就返回 
            我们可以使用循环  到我们设定的条件循环结束
            第一种方案
                while True: #死循环
                    if 在首页:
                        break # 条件成立退出循环
                    执行返回 #  否则一直死循环 
            第二种方案
                while 不在首页: 
                    执行返回 # 条件成立 说明 不在首页 执行返回   不成立的话 说明在首页了代码进不来
新的问题
10. beautifulreport报告渲染失败 没有样式 
    原因 通过F12抓包可以发现 我们的beautifulreport会自动触发一些网络请求得到css js文件 但是其中
        有两个请求访问失败 导致样式加载不出
            https://stackpath.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css
            https://stackpath.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js
    解决方案: 这个两个文件找一个国内可替代的网站进行更新即可
        我们不能直接更新最终生成的报告的地址路径(下次在输出报告 还需要更新)
        所以直接更新模板  后续每次在渲染新的报告时就会使用修改后的地址
        所以修改模板页面(BeautifulReport框架目录下template下的template.html)的 第6行和7120行 分别将地址进行替换
            http://101.132.111.36:8080/css/bootstrap_3.3.5.min.css
            http://101.132.111.36:8080/js/bootstrap_3.3.5.min.js
    新的问题:有时候这两个都不稳定,如何解决
    解决方案:主动权交给自己 不在去访问别人网站的css js  将这些文件先提前下载到本地 然后代码中直接访问本地的数据既可
        将一些静态资源部署在公司某台服务器上 后续操作直接访问自己公司得服务器资源即可
新的问题            
11. 虽然每个case不会受到影响,且都能正常执行了(因为添加了一段无论是否异常都回到开始页面的逻辑)
    但是报告都是显示成功 unittest的汇总报告都是显示通过
    airtest的报告也都是通过 而且 把回到首页的这些步骤也记录在报告中
    问题原因: 
        unittest报告显示通过的原因:
            unittest单元测试框架 本身就有异常捕获  如果case出错 那么unittest会捕获错误 并标记case为错误case 然后执行后面的case
            但是现在你的case内部使用了异常捕获(内部消化了异常)  导致unittest框架的确没有捕获到错误
    解决方案:
        捕获完异常后在抛出异常 但是 我们之前使用异常的目的 是为了让回到开始界面的逻辑 无论怎么样都要执行
        所以 我们可以在 except Exception as e 下面 抛出异常  然后在finally写 无论怎么样都要执行的业务逻辑
        try:
            业务逻辑
        except Exception as e:
            print(e)
            raise e  # 将捕获的异常再次抛出
        finally: # 无论代码是否出现异常都要执行finally
            无论怎么样都要执行的业务逻辑
    问题原因:
        airtest报告显示通过的原因:
            airtest之前能看到失败主要是因为我们有些断言的场景失败了
            但是我们现在的错误都是元素定位不到或者其他一些错误 代码走到这一步就报错然后走入异常捕获机制
            airtest并没有记录这个错误
    解决方案:
        如果代码出错 让airtest知道自己出错
    实现:
        airtest提供一个 log(arg, timestamp=None, desc="", snapshot=False) 方法 
        既可以实现 错误提示 也可以实现一些代码的注释
        参数含义:
            arg:    根据用户输入的内容不一样 会自动判断
                如果传入一个异常信息 airtest就会标记为错误
                如果传入一个普通字符串  airtest就会认为这只是一段提示信息
            timestamp:时间戳 默认用当前时间点  默认即可
            desc:描述信息
            snapshot:默认为False 开启为True会自动截图
        所以如果出现异常了  在抛出异常之前 先 log(e) 让airtest知道case出现异常 并记录该异常原因 这样最终airtest就可以显示错误了
新的问题:
12. unittest报告和airtest报告没有任何关联
    unittest是我们的汇总报告 airtest是我们的详情报告 站在用户的角度 我们应该提供一个报告的详情链接地址 通过这个地址直接链接到详情报告
    而不是人为的输入地址
    需求:最好在汇总报告页面上 有个连接 可以让我们直接访问到详情报告处
    解决方案: 所有的输出 BeautifulReport 都会记录并显示在报告上
优化方向:
13.代码冗余  不简洁 后期代码维护极度不方便
    我们应该框架完成后 重点在业务逻辑编写 
    思路:共同的部分封装函数  
    实现:使用三层闭包 的装饰器 封装冗余代码。
        使用三层闭包原因在于我们装饰器还需要接收一个文件名的参数
细节优化
14.unittest报告上的描述式null  如何让他可读性更高
    beautifulreport 会记录每个case运行过程中的数据 并保存在内存中
    等到所有case执行完成  再去获取数据进行测试报告的渲染
    报告上的描述信息 主要是 受 case对象.__dict__['_testMethodDoc']影响
    所以如果想在页面上显示描述信息 
    只需要
    self.__dict__['_testMethodDoc']=描述信息即可
    self表示的就是case对象
15.log文件夹 存在历史数据  
    历史数据  没有 必要保留 
    实现思路:每一次case执行前 把历史数据清空
    实现:
        将原本的创建文件夹的逻辑进行重现 实现  每一次case执行前 把历史数据清空
            if os.path.exists(dirpath):# 条件成立 说明 文件夹不存在
                shutil.rmtree(dirpath)
            os.mkdir(dirpath)
相关技术点:
    if  条件  elif 条件  while  条件  条件判断的本质就是看条件的布尔值属性是否为真
        为真表示条件成立 代码执行   为假表示不成立不执行
        python万物皆对象  每个对象都有布尔值属性 除了空对象(空字符串 "",空列表 [],{},) None 数值为0的数字 False布尔值为 False外其他都为True
    逻辑运算符 and or not 
    not 表示取反 针对后面表达式的布尔值属性进行取反 得到的结果不是 True就是False 
    and 逻辑与 并且 两者都   表达式1 and 表达式2
        如果表达式1的布尔值属性为真 直接 输出表达式2的结果 如果为假 直接输出表达式1的结果
    or  逻辑或 两者任意 任意一个满足 表达式1 or 表达式2
        如果表达式1的布尔值属性为真 直接 输出表达式1的结果 如果为假 直接输出表达式2的结果
    优先级 not >and > or
    sys.argv 获取命令行传递过来的参数 返回的是一个列表 
    右击run运行python脚本的本质 其实就是 pycharm在终端 使用你配置的环境 运行当前你要运行的脚本 
    os.path.isfile(basedir): # 判断传递的参数是否为文件 返回True或False
    os.path.exists(dirpath)  判断当前传递的参数路径是否存在  存在返回True 不存在返回False
    is 本质就是判断两个东西的内存地址是否一致  id(a) == id(b)
    == 判断两个东西数值是否一致 
    浅拷贝  元素.copy()  最外层的引用地址发生改变 但是元素内部的引用地址不会变化
    深拷贝  完全的复制数据 但是所有的引用地址都会发生改变
    python内存池概念 将常用的数据(-5,256,True,None)在运行一开始就已经分配好内存无论后面是否变化都已经生成好该数据的内存地址
    后期只要引用 就使用该数据固定的内存地址
    while循环   对于已知条件进行循环
    for循环     对于已知变量已知次数的场景我们可以使用for循环
    生成报告的流程
    1.收集数据
        收集case运行过程中产生的相关数据 (caseid,所属模块,case描述信息,标题等你需要的信息)
            数据存储的方式:
                持久化存储(文件形式, 数据库形式存储)
                内存存储(直接将相关数据存到内存中)
    2.渲染模板
        数据收集完成后 将数据填充到模板页面(母版 最基本的框架  后期只要有数据填充即可)isinstance(对象,) 判断对象是否属于该类  涉及类的继承
    定义函数  def 函数名(参数):
    调用函数  函数名()
    函数名本质就是和变量名类似  变量名是用来指向具体的一个数据  函数名是用来指向具体的一段代码逻辑
    当函数名进行赋值的时候 其实也是在传递函数的引用地址
    装饰器:  @函数名   当
        代码从上到下走到这一步 代码会执行  函数名(被修饰的函数) 得到结果覆盖点原有的函数 所以我们叫这一个过程为装饰
    装饰器函数本质就是 闭包函数 
    万能装饰器写法:
        def outer(func): # func用来接收被修饰的函数
            def inner(*args,**kwargs): # *args,**kwargs 用来接收被修饰函数的参数 无论什么参数 *args,**kwargs都可以接收
                res = func(*args,**kwargs) # func() 调用真正意义上被修饰的函数   *args,**kwargs将参数解包 原理怎么传现在怎么用  res 的目的是为了解决有的原函数可能会有返回值情况
                return res # 返回返回值(有的被修饰的函数可能存在返回值)
            return inner # 返回内部函数的引用 用来覆盖被修饰的函数
    三层闭包实现的装饰器 主要就是解决装饰器需要传参的场景
    def get_param(形参):
        def outer(func): # func用来接收被修饰的函数
            def inner(*args,**kwargs): # *args,**kwargs 用来接收被修饰函数的参数 无论什么参数 *args,**kwargs都可以接收
                res = func(*args,**kwargs) # func() 调用真正意义上被修饰的函数   *args,**kwargs将参数解包 原理怎么传现在怎么用  res 的目的是为了解决有的原函数可能会有返回值情况
                return res # 返回返回值(有的被修饰的函数可能存在返回值)
            return inner # 返回内部函数的引用 用来覆盖被修饰的函数
        return outer
    @get_param(实参)--->@outer
    装饰器应用场景 : 1.解决代码冗余
                   2.现有功能要实现拓展       
                        开放封闭:已经实现功能的代码不允许被修改,但可以外部拓展     
                   3.常用实际场景:日志处理  性能计算   等                  
    函数参数
        实参  调用函数时传递的参数叫实参 具有实际意义
            位置参数    按顺序传递 
            关键字参数  给指定的形参传入指定的值   格式   形参名=值 
        形参  定义函数(def)时写的参数叫形参
            位置参数  按顺序接收  
            默认值参数 如果该参数没有值接收 可以使用默认值 而不会报错  格式 形参名=默认的值
            不定长参数
                *args       接收没人要的位置参数   打包成一个元祖  没有的话就是空元祖
                **kwargs    接收没人要的关键字参数  打包成一个字典  没有的话就是空字典
                我们可以通过*args和**kwargs接收所有的实参
        注意点: 如果* 号出现在调用函数的时候 说明是解包操作   将数据拆分成多个位置参数
               如果 **号出现在调用函数的时候 说明是解包操作 针对字典解包 解包成关键字参数 
    闭包函数  函数的返回结果是内部函数的引用
        闭包函数 内部函数可以直接使用外部函数的参数
                但是不能修改外部函数的参数
                如果你非要修改 使用 nonlocal 声明 该变量为外部函数变量
    匿名函数  lambda  lambda a,b:a+b    冒号前面表示参数  冒号后面表示返回的结果
    递归函数  函数内部调用自己  递归函数必须要有结束条件

你可能感兴趣的:(python测试开发笔记,自动化,python,运维)