RF

1:rf测试框架:

  rf基于python编写的通用型自动化测试框架  和pytest功能相同(rf什么都可以做,类似unitest但是比unitest强大很多)
  框架:实现特定需求和功能的软件的解决方案工具包(slenium,appium,requests都是框架)
  和pytest相比语法不同,rf是关键字驱动(可以不用写python代码)pytest需要用到python编码知识
  rf的学习曲线比pytest 先难后易,rf有自己的语法,主要学习rf的使用技巧和语法

  rf自带测试报告功能,无需安装插件,
  rf可以筛选测试用例和套件,和pytest差不多
  rf定义了灵活且易理解的测试用例执行控制(包括环境初始化和清除)
  rf具有清晰的日志和报表功能,让我们可以清晰看到测试执行结果

2:python编码掌握工具结合项目完成自动化测试会面临如下问题:

  自动化测试面临的问题 使用rf框架解决以下问题
    如何组织大量自动化用例 单个文件写一条用例还是函数封装好,还是说不太模块不太目录进行封装,如何组织
    如何合理的进行初始化清除 用例运行前置条件--setup teardown 初始化和清除,用例嵌套,如何对公共的数据做初始化清除--unitets搞不定---
    如何批量筛选待执行的用例 只跑一部分用例,回归测试用例,冒烟测试用例,批量刷选---定制化执行
    如何输出清晰的测试报告

3:unitest适合单元测试,rf适合系统测试  

4:RF优点

  它定义了灵活且简易的初始化和清除环境
  它可以方便挑选要执行的用例和套件(名称、标签过滤)
  它有清晰的日志和报表功能,让QA和manager可以清楚的查看测试执行结果 ----自带日志和报表功能

5:用例过程中出现一些问题,怎么直到问题是被测环境的问题还是代码的问题:

  通过log日志,rf框架测试过程中会自带日志,通过日志快速定位问题以及问题原因,(查看代码报错信息和装饰器加报告查看报告信息)

6:RF环境安装

  一:安装rf库:环境安装-运行环境     robotframework 本质上是python编写的自动化测试框架,安装库就行
    RF的安装: pip install robotframework     rf是python语言编写的框架,是一个库,用途上是自动化测试框架
    seleniumlibrary的安装
      支持Selenium自动化的 RF扩展库
      pip install robotframework-seleniumlibrary -U     

         #加-U参数  robotframework-seleniumlibrar依赖selenium, -u把selenium也更新一遍,不仅仅使用最新的
         #robotframework-seleniumlibrary -U也会把依赖的selenium也更新一次

  二: 编辑环境:Pycharm编写 +IntelliBot插件支持rf语法高亮

    intellibot .jar     一个jar包
    pycham——>settings——>plugins——>install from disk 导入IntelliBot插件
    如果pycham安装了插件还是不得行:重写安装插件,
    检查robot文件类型是否被其他文件类型占用,或者直接在robot文件类型下面添加*.robot这样就可以了
    RIDE 工具写代码,不建议使用

7:rf文件结构:

  1:robot文件:套件文件,套件文件不能嵌套,pytest文件(有类,模块文件,有包)
    rf文件:文件嵌套只有套件目录和套件文件  本质上都是套件,套件目录可以嵌套文件,目录可以嵌套目录
    套件目录只要目录中有包含测试用例的文件既可,套件文件里面定义测试用例)

    pycham创建rf文件:file--XXX.robot(文件名以robot结尾就可以)
      rf默认文件编码格式必须utf8,不然有中文会报错

8:关键字(keyword)形式驱动:关键字本质也类似python函数

  标准库提供了常用的功能       rf自带的
  第三方扩展库              安装第三方库文件
  开发者根据产品自行开发库        自己python编写的
    自动化框架、库开发者
    自动化用例开发者

9:rf用例定义在表格中,使用pycham文本模式编写,和表格模式对比文本模式编写效率更高,有的公司可能使用ride  

    *** Test Cases ***                            #用例表,首字母大写,中间有空格---插件自动识别,补齐功能    
    用例1                                          #用例标题,顶格写
        log to console  hello robot                #用例主体部分:由关键字组成,和文档边缘需要空两格以上,关键字与参数也需要空两格以上
                                                    多个参数,参数与参数之间也空两格以上,两个或者两个以上的空格是rf的单元格分隔符
        log to console  hello pytest                #第二步另起一行,就是么简单,步骤三,步骤四都写一行,逻辑上还是表格的,2个空格为下一个表格
                                            
    用例2                                            #用例2的标题

10:C:\Users\ywt>where robot     #robot和pip在同一目录的 ,查看robot文件的目录

  E:\Program Files\Python36\Scripts\robot.exe

11:执行rf用例:

    1:执行robot文件:
        robot 文件名.robot(命令行在文件的同级目录下)
        robot 文件路径(绝对路径或者相对路径)
        cmd到robot文件所在的文件夹:            C:\Users\ywt\Desktop\RF_study>robot test1.robot                 直接在robot文件路径运行(命令行在文件的同级下)
        robot+文件名运行
                                            C:\Users\ywt>robot C:\Users\ywt\Desktop\RF_study\test1.robot    指定robot文件绝对路径运
                                                        
                                            C:\Users\ywt\Desktop>robot RF_study\test1.robot                 robot指定相对路径也可以,
                                                                                                         当前目录下的RF_study文件夹的test1.robot文件
                                            C:\Users\ywt\Desktop>robot .\RF_study\test1.robot            ./的相对路径也行
                                            C:\Users\ywt>robot Desktop\RF_study\test1.robot

    
    2:rf执行目录套件(和执行文件一样的)
                                            C:\Users\ywt>robot C:/Users/ywt/Desktop/RF_study/                RF_study目录下的所有的测试用例文件都执行
    
        robot执行文件的格式:选项永远在中间 ,用例文件/目录 永远在最后的
            robot  <选项> 用例文件/目录                目录可以是绝对路径也可也是相对路径,一般使用相对路径
        
        目录执行顺序是展开之后从上到下的一个顺序
    
    3:rf执行命令的选项
        robot -help
        

    rf命令执行之后会自动生成report测试报告,每次执行命令之后输出三个文件,三个文件,在执行后可以看到打印,默认在命令行的当前路径生成测试报告
        Output:  C:\Users\ywt\output.xml                    #测试用例数据都在xml里面,log和report等都是一些样式代码,看到的数据都是从xml来的,
                                                                数据剪切二次开发需要修改xml文件----bs解析xml神器
                                                                    
        Log:     C:\Users\ywt\log.html                        #日志
        Report:  C:\Users\ywt\report.html                     #报告
    
    rf关键字驱动,rf强大在每一个关键字都有日志记录,在log文件里面,关键字名称和关键字参数都能记录到
        关键字报错log里面能够看到
        
    rf自己提供了日志模块,
    

12:rf常用关键字:用例执行的主体,关键字本质是python函数,

  1:关键字类型除了标准库中自带的,rf自带的    标准库
  2:还有第三方库提供的(也是python函数编写的),
  3:自己用python代码写关键字(自定义库)

 

13:rf导库需要先定义一个表,在*** Settings ***表中进行导库动作,

  Library SeleniumLibrart      #库名称 SeleniumLibrart不能写错,大小写敏感

 

 

14:rf编写百度松勤的测试用例--采用seleniumLibary关键字完成

    *** Settings ***
    Library  SeleniumLibrary                #库名称 SeleniumLibrart不能写错,大小写敏感
                                            #Library  空两格以上+库名称
                                            #Library代替import

    *** Test Cases ***
    百度测试
        open browser    https://www.baidu.com/  chrome               #打开浏览器并访问到百度
        set selenium implicit wait  10                               #隐式等待
        input text  id=kw   松勤\n                                   #输入框输入松勤\*** settings ***
        ${res}   get text  id=1                                      #获取第一个搜索结果文本声明变量res保存
        should contain  ${res}  松勤网 - 松勤软件测试                  #校验,判断文本中包含,前者包含后者
        close browser                              #关闭浏览器

15:关键字本质是函数,为什么能使用rf来执行,

  robot自动测试用例文件,robot执行的时候,实际上robot是一个python程序,把robot里面的文件

  内容读取进来,文件内容类似文本文件文本文件按照一定格式来写才能正常识别,匹配--rf框架的作用,测试文件内容读取进来之后

  去看用例主体哪些关键字,最终调用的是关键字,--关键字是python函数,最终调用的就是python函数

  函数封装好了之后用rf格式写用例降低编写代码的难度---rf比较好学,几个关键字就可以把自动化做起来,降低初学者门槛

  rf读取用例文件,将内容做一下解码,文件编码必须utf8的,rf内部编码就是uft8,gbk等编码识别不了中文

16:rf用例文件特点:

  1:套件文件类型:表格式,纯文本格式
  2:表格式文件扩展名csv或tsv
  3:存文本扩展名可以是.robot或者.txt,RF官方推荐使用.robot
  4:存文本类型一定要保证用例文件的编码格式必须是UFT8

17:RF框架中的四种表:
  1:*** Test Cases ***表      用来定义用例
  2:*** Settings ***表          1:导入库 2:定义初始化和清除 3:定义标签 4:导入资源文件(robot语法库文件) 5:定义用例模板
  3:*** Variables ***表        用来定义公共变量的
  4:*** Keywords ***          用来定义用户关键字(rf语法中的库)

18:rf里写的所有字面量常量,

  不管写的数数字类型10还是字符串类型,统统都是字符串,传整数类型需要转化:${0} 这样转化是代表整形0

19:导入库和各种变量最好以一个点当开始写相对路径:

  一般是根据文件自身所在的目录为相对目录起点,一般以项目根目录为路径起点

20:robot文件使用自定义关键字

RF_study                #文件根目录
    pylib
        ywtlib.py
            import requests
            def login_lesson():
                payload={"username":"auto","password":"sdfsdfsdf"}
                header = {"Content-Typ": "application/x-www-form-urlencoded"}
                reps=requests.post("http://localhost/api/mgr/loginReq",data=payload, headers=header)
                reps.encoding = 'unicode_escape'
                return reps.cookies

            def get_lesson_list():
                cookie=login_lesson()
                resp=requests.get('http://localhost/api/mgr/sq_mgr/?action=list_course&pagenum=1&pagesize=20',cookies=cookie)
                return resp.json()

            if __name__ == '__main__':
                print(get_lesson_list())
    
    自定义关键字.robot
        *** Settings ***
        Library  pylib/ywtlib.py

        *** Test Cases ***
        获取课程名称用例1
            ${res}    get_lesson_list
            log to console  ${res}
            should be equal     ${res}[retcode]     ${0}                #${res}[retcode] 取变量res的键为retcode的值,
                                                                        #${0}这样写转化是代表整形0
            should be equal as integers  ${res}[retcode]     0          #前后两变量都当成整形比较
            should be equal as strings   ${res}[retcode]     0          #这样也行,前后都转字符串
            #rf中所有的常量参数都是作为字符串传递到关键字中的

21:rf关键字,变量,循环

  关键字使用方法查看:
    1:rf官网查看官方文档:https://robotframework.org/#libraries
    2:通过查看关键字对应的代码(源码)

  C:\Users\ywt\Desktop\RF_study>robot -t 百度测试 test1.robot -t筛选用例
  C:\Users\ywt\Desktop\RF_study>robot -t 传递参数 关键字的使用.robot
  C:\Users\ywt\Desktop\RF_study>robot -t *2 关键字的使用.robot -t筛选用例可以使用通配符*

  open  browser  https://www.baidu.com/  chrome  executable_path = E:\Program Files\Python36\chromedriver.exe
    executable_path:指定浏览器驱动路径,缺省值参数传递: executable_path = E:\Program Files\Python36\chromedriver.exe

  常用的关键字:

    log                                  log体现在log日志里面,打印在日志里面,调试可以使用,不管字符多长都能打印

    log to console                       打印在控制台上面                                    
        
    log many +可变关键字                    打印多个,后面跟多个参数,在log里面添加信息
    
    log many    今天\ 天气  很好        假如传递的字符串,今天    天气,这个字符串包含多个空格,从第二个空格处前面用反斜杠\修饰,
                                          需要使用\替代一个空格,手打的方式,很麻烦,放在变量里面来解决,或者python语法来处理
                                         一个反斜杠只能修饰一个空格,如果遇到这种情况使用python来处理    
    
    ${var1}     set variable    hello                     #定义变量
    
    ${num}      convert to number   10                    #整数类型的字符转化成浮点数,--定义一个浮点数
    
    ${num}      convert to integer   10                              #整数类型的字符转化成整数,--定义一个整数变量
    
    ${sums}     get_sum     ${2020}                              #${2020}这样写就是整数2020,${2020.0}代表浮点数,
    
    Library        导入关键字库
    
    import library  testlib.py                                  #当前用例单独使用该库的关键字吗,其他测试用例不得行
                                                                    #这种方式需要cmd执行的使用需要加个参数p指定python path所在的目录才能找到库文件
                                                                    
                                                                    
    sleep                                                    #    sleep  3这样直接使用
    
    should contain                         包含的关键字,前面包含后面,断言判断预期结果和实际结果是否相等,是否包含的场景---断言关键字should be
    should be equal                        判断两个对象是否相等,类似python两格等号
    should be equal as strings             指定类型判断,将两个参数对象转化成字符串类型比较
    should be equal as integers            将给定的两个参数对象转化成整数类型比较是否相等
    
    should be true                      这个关键字比较特殊,传一个参数,参数是一个python(条件)表达式,python语法
                                            表达式不能有两个以上的空格,受rf语法的限制
                                            should be true的原理是python的    eval    函数,接收字符串,字符串
                                            可以是python语法或者python语法的表达式,传个字符串类型
                                            表达式结果为真断言通过,不为真断言失败
                                            断言的特点如果失败不会执行后面的语句

  rf使用关键字的时候可以忽略下划线(如果关键字函数的源码有下划线,下划线可以省略或者用空格代替)

    带上下划线也没事,关键字转化成字然语言,get_type可以在rf中get type这样调用

  实参传递:
    前面传递参数用的都是字面量,字面量就是常量,常量都是unicode类型的字符串,编码方式utf8,

22:常用关键字实例:

    testlib.py    
        def get_type(var):
            return type(var)

        def get_sum(a):
            return a+10
        

    关键字的使用.robot    
        *** Settings ***
        Library  testlib.py               #当前测试套件内都可以用该库的关键字

        *** Test Cases ***
        常用关键字
            import library  testlib.py      #当前用例单独使用该库的关键字吗,其他测试用例不得行
                                            #这种方式需要cmd执行的使用需要加个参数p指定python path所在的目录才能找到库文件
            log  今天天气很好
            log to console  天天气很好
            log many    今天  天气  很好
            log many    今天\ 天气  很好

        传递参数
            ${var1}     set variable    2020                                #定义变量,传的参数是2020数字,但是这里最后还是当字符串
            ${var2}     set variable    [1,2,3,4]                           #这样传递的是一个列表,但是最终还是当成字符串,rf定义字符串
                                                                            #不需要引号,常量就是字符串,不需要加引号
            ${num}      convert to number   10                              #定义一个浮点数
            ${num}      convert to integer   10                              #整数类型的字符转化成整数,--定义一个整数变量
            ${sums}     get_sum     ${num}

            ${sums}     get_sum     ${2020}                                     #${2020}这样写就是整数2020,${2020.0}代表浮点数

            ${sums}     get_sum     ${[1,2,3,4]}                             # ${[1,2,3,4]}这样写列表类型不写,数据太复杂--这种方式不支持
                                                                            #简单的int,float可以转化过来使用


            log to console  ${sums}
            log to console  ${var1}
            ${res}  get_type    ${var1}
            log to console  ${res}

        常用关键字3
            log to console  "hello"
            sleep  3
            log to console  "python"
            should contain
            should be equal
            should be equal as strings
            should be equal as integers
            should be true


        常用关键字4
            ${except}     set variable    hello
            should be true       ${except}=="hello"         #rf变量表示的字符串传递给python时会去掉引号----
                                                            #rf这里定义except的时候是hello,没有加引号,所有需要主动加引号,如下
            should be true       '${except}'=="hello"       #这样手动加引号就可以了,这样做比较麻烦
                                                            #python表达式里面引用rf变量,前面加$就可以了,不需要加的大括号,如下
                                                                #这样也可以代表变量except        并且里面的字符串传递到python主动加上
                                                                #引号
            should be true       $except=="hello"
                                                            #普通的关键字传参,只能用普通的形式,should be true    这个关键字特殊
                                                                #因为里面传递的参数是python表达式
                                                                #只有在python表达式里才可以用$变量名这样形式的参数
            log to console      ${except}                   #这样写没问题
            log to console      $except                     #这样写$except 变成一个常量,一个字符串,rf解析不了这种参数,除非在python
                                                            #表达式里面

            should be true  "he" in "hello"                 #表达式不能有两个以上的空格    

 23:测试库: 

  python模块文件:1:类的方法
            2:函数

  引用方法:1:内置库无需引用
           2:声明引用:1:直接导入
                2:带参导入

  类型:1:标准库:rf装好就有1:内置库:关键字可以直接使用
                2:非内置库需要导入才可以使用的关键字
        2:第三方库:需要安装或者自行编写:如seleniumLibary 自定义库)

        3:自定义关键字 -自己写的python模块函数,类等

24:rf导入库的方法:

*** Settings ***
#Library  ywtlib2.py                #导入方式1,导入模块的文件名---模块导入法,适合初学者,
Library  ywtlib2                    #导入方式2,这种类似python导入库函数,寻找库的路径类似pythonpath
            
rf本质上也是python程序,寻找库的方式一模一样,
    python根据PYTHONPATH寻找库,默认的PYTHONPATH:如下
          E:\Program Files\Python36\Scripts\robot.exe
          e:\program files\python36\python36.zip
          e:\program files\python36\DLLs
          e:\program files\python36\lib
          e:\program files\python36
          e:\program files\python36\lib\site-packages                安装好的库,pio安装好的库路径一般都在lib里面
          e:\program files\python36\lib\site-packages\win32
          e:\program files\python36\lib\site-packages\win32\lib            
          e:\program files\python36\lib\site-packages\Pythonwin            
            
    我们自定义的库在RF_study当前目录,当前目录没有添加到PYTHONPATH里面来,所有需要指定-p参数来指定PYTHONPATH路径,否则找不到
        ywtlib2  这个库处于RF_study目录下,    
            
    
    所以执行的适合需要指定pythonpath寻找测试据库的路径:
    C:\Users\ywt\Desktop\RF_study>robot -P . 自定义关键字.robot               #-P . 指定当前路径RF_study为pythonpath,robot文件里面的Library  ywtlib2导入库
                                                                              就会从当前路径RF_study寻找ywtlib2库文件,就能找到了
                                                                              .相等路径的意思,当前路径,也可也使用绝对路径
            
    ywtlib2移动到其他文件夹,在    RF_study\pylib文件夹里面
    C:\Users\ywt\Desktop\RF_study>robot -P ./pylib 自定义关键字.robot            这样执行就可以了,指定./pylib,当前路径下的pylib为pytonpath路径就可以了
    C:\Users\ywt\Desktop\RF_study>robot -P pylib 自定义关键字.robot                这样也行,   ..表示上级目录, .表示当前目录
        
        写相对目录需要知道相当目录当前的起点的,相对目录的起点就是当前目录:C:\Users\ywt\Desktop\RF_study>
            RF_study这个目录,寻找pylib路径直接当前路径就可以找到pylib,ywtlib2库文件在pylib目录里面
            只需要把pylib加到pythonpath里面就可以,.是当前目录,放在当前目录下的pylib里面了,变成pylib就可以了,因为当前路径为起点就能找到pylib,
            pylib在当前路径之际可以找到
            
            
    模块导入发固定一个原则:
        C:\Users\ywt\Desktop\RF_study>robot -P . 自定义关键字.robot                    #我们都以    -P .  当前路径为pythonpath
            
        在模块的settinbg里面    Library   pylib.ywtlib2        导入时候去写以RF_study为pathonpath导入
            ywtlib2文件在RF_study/pylib        目录里面
            
            Library   pylib.ywtlib2  robot文件里面这样导入文件就可以了,RF_study为pythonpath,RF_study下找pylib/ywtlib2可以找到就ok
                        
    相对路径吃透,相对路径的起点,一般是命令行的当前目录,以他为准。    C:\Users\ywt\Desktop\RF_study>  当前目录是RF_study路径
        类似python导包,关键字大小写不敏感。测试库搜索路径和python程序一样,sys.path可以查看

25:rf中的变量

    本质:python对象 
    
    形态:1:scalar        ${var}直接传递python对象                     多了符开头:传参直接传递python对象,整体传参
          2:list         @{var}展开列表内的元素,作为多个参数传入        展开容器元素传参,类似python里面的解包    
          3:Dictionary &{var}展开字典内的元素,作为多个参数传入          &=and符号
          
    RF变量.robot
        *** Test Cases ***
        列表类型
            #定义变量时候无需写成拆包形式,写成拆包形式也没什么影响,和定义变量什么形式没有关系的
            ${list}     create list  a      b       c
            log to console  ${list}                 #打印['a', 'b', 'c']
            log to console  @{list}                 #不会打印,log to console第一个参数才是输出的值,第二个参数是输出流,
                                                    #如果第二个参数传参了,不是stdout标准输出,那么不会输出在控制台上面
                                                    #出口导偏了
            log many  @{list}                       #a b c   拆成三个参数输出,类似列表元素解包了,类似python里面的*
            log many  ${list}                       #    ['a', 'b', 'c']打印一个整体
            log many  ${list}[0]                    #获取列表里面某个元素用下标
            log many  ${list[0]}                    #下标写在中括号里面也是一样的,符合python语法的,里面可以计算,list[0]取下标第一个

        列表类型2
            @{list}     create list  a      b       c
            log many  @{list}                       # a , b c               @{list} 拆包传参,作为多个参数
            log many  ${list}                       #输出['a', 'b', 'c']    ${list}  整体传参,作为一个参数
            log many  ${list[0]}                    #输出:a

            #定义时候什么类型和传参的时候什么类型没有关系的,传参形式只和传参时候采用的类型是有关系的,传参的时候
            #用的scalar类型就是作为一个整体

        字典类型3
            ${dict}     create dictionary  a=1      b=2     c=3
            log many  ${dict}                       #{'a': '1', 'b': '2', 'c': '3'}
            log many  &{dict}                       #输出a=1,b=2,c=3  键值对类型
            log many  ${dict}[a]                    #通过键取值,不需要加引号,只有python表达式需要加引号
            log many  ${dict["a"]}                  #下标放里面需要加引号,大括号里面是按照python语法来解析的
                                                     #python语法里面字符串是有引号的

26:RF循环:

  编程语言几大要素:1:变量 2:函数(关键字) 3:循环 4:条件判断

RF循环.robot    
    *** Test Cases ***
    循环1
        FOR     ${i}    IN      a   b   c   d
        log to console  循环体${i}
        END         #循环体结束

    循环2
        ${list}     create list  a   b   c    d
        FOR  ${i}  IN    @{list}            #需要写成拆包的形式,将元素展开,写成${list}  这种形式不拆包只循环一次,作为一个整体
        log to console  循环体${i}
        END         #循环体结束

    循环3
        #IN RANGE移一定要大写,并且中间只能有一个空格,一个空格打印10次,从0-9
        #如果是IN  RANGE有两个空格,执行不会报错,只循环两次,RANGE一次,10一次  range和10当成待遍历的选项了
        #千万注意一个空格
        FOR   ${i}    IN  RANGE   10
        log to console  循环体${i}                     #循环体${i}这个类似python字符串的拼接
        END         #循环体结束

    循环4
        FOR   ${i}    IN  RANGE   1    11               #循环1-10,左含又不含
        log to console  循环体${i}                     #循环体${i}这个类似python字符串的拼接
        END         #循环体结束

    循环5
        FOR   ${i}    IN  RANGE   1    11     2        #步长模式,遍历1,3579
        log to console  循环体${i}                     #循环体${i}这个类似python字符串的拼接
        END

        #一般复杂的逻辑交给python实现。rf调用就行,rf循环嵌套不了,语法弱,就一层    

27:RF 条件判断,初始化与清除

  pytest现成的python语法。rf自己的语法,简单的编程语言
  编程语言几大要素: 1:变量 scalar类型,可以是字典,可以是字符串,可以是整数
            2:函数(关键字) rf里面的关键字起始就是python里面的函数
              3:循环 FOR循环
              4:条件判断

run keyword if语句
    RUN KEYWORD IF  RF使用RUN KEYWORD IF关键字做条件判断    if后面+条件表达式,if后面为真则执行后面的语句,不为真不执行
    
    RUN KEYWORD IF    condition    
    def run_keyword_if(self, condition, name, *args):        源码定义:形参condition:python条件表达式,符合python语法,
                                                                        name:表示条件成立执行关键字的名称,如果关键字需要参数,关键字进行传参
    
        condition:条件表达式,should be true 关键字的参数也是condition条件表达式,
                    should be true是断言类型的关键字,如果condition不成立,则报错
                        
        RUN KEYWORD IF  1>0     log to console  正确            最简单的
        RUN KEYWORD IF  1==0     log to console  正确            条件不成立
        
    
    1:普通的run keyword if
        *** Test Cases ***
        条件
            ${var}    set variable  hello rf
            run keyword if  'rf' in $var and 'hello' in $var   log to console  正确                #两个条件都成立才会执行打印的语句
            
            run keyword if后面+condition。也是python表达式,所以可以写成$var和'rf'引号形式
            'rf' in $var and 'hello' in $var    python语法,和python逻辑一模一样
            
        后面要执行的语句占用的篇幅太多,这时候可以做一个逻辑上的换行,rf上还是同一行:如下
            *** Test Cases ***
            条件
                ${var}    set variable  hello rf
                run keyword if  'rf' in $var and 'hello' in $var                       #$except和$var这种形式只有在python表达式里面能够解析
                ...     log to console  正确

             log to console  正确还是上面一行的语句,换行的话三个点+两个以上的空格(三个点类似占位符)这两行在rf逻辑上还是一行的
    
    2:条件判断-else
        条件判断-else
            ${var}    set variable  hello rf
            run keyword if  'abc' in $var and 'hello' in $var    log to console  in the var
            ...  ELSE   log to console  not in the var
            #这里abc' in $var and 'hello' in $var条件不成立执行else语句

    3:run keyword if+else语句
        条件判断-else-if
            ${var}    set variable  hello rf
            run keyword if  'abc' in $var and 'hello' in $var    log to console  in the var
            ...  ELSE IF   'rf' in $var  log to console  find rf
            ...  ELSE   log to console  not in the var

            #else if 在else前面,else是最终的选择结果,兜底的选择

    上面就是rf三种条件判断if语句基本分支
    
    4:条件判断--嵌套
    条件判断--嵌套
        ${var}    set variable  hello rf
        run keyword if  'hello' in $var
        ...  run keyword if   'rf' in $var
        ...    run keyword if   'he' in $var    log to console  三层嵌套    
    
    嵌套if不建议在rf里面使用。一般在python里面实现,复杂逻辑就是python是实现


    5:rf里面使用for循环没有while循环,使用一个很大的for循环来模拟while循环,如下
        条件判断3--FOR循环判断
            FOR  ${one}  IN RANGE   9999
            ${score}    get value from user  请输入你的分数0-100       #message必填参数,提示符合,get value from user类似python的input函数
            #如果用户输入exit就退出
            run keyword if   $score=='exit'     exit for loop           #exit for loop==python的break,退出本层循环
            chek score  ${score}
            END

rf    for循环里面的continue和exit机制

    循环里面的exit操作,exit for loop        ==exit
        条件判断3--FOR循环判断
        FOR  ${one}  IN RANGE   9999
        ${score}    get value from user  请输入你的分数0-100       #message必填参数,提示符合,get value from user类似python的input函数
        #如果用户输入exit就退出
        run keyword if   $score=='exit'     exit for loop           #exit for loop==python的break,退出本层循环
        chek score  ${score}
        END
        
    run keyword if   $score=='exit'     exit for loop          这一句有个简写,如果run keyword if后面执行的break如下写:exit for loop if  
        exit for loop if  $score=='exit'
    

    循环里面的continue操作:continue for loop 
        条件判断31--FOR循环判断
            FOR  ${one}  IN RANGE   9999
            ${score}    get value from user  请输入你的分数0-100       #message必填参数,提示符合,get value from user类似python的input函数
            #如果用户输入exit就退出
            run keyword if   $score =='exit'     exit for loop           #exit for loop==python的break,退出本层循环
            #如果用户输入continue就继续循环
            run keyword if  $score == 'continue'   continue for loop       #
            chek score  ${score}
            END    

        run keyword if   $score=='exit'     continue for loop          这一句有个简写,如果run keyword if后面执行的continue如下写:continue for loop if  
            continue for loop if  $score=='exit'

        set variable if      如果条件成立就赋值,相当于run keyword if    +    set variable 结合,简化的写法
            原理还是一样的


run  keyword    if    和should be true两个关键字,第一个参数都是python'条件表达式,底层通过eval函数实现的,可以直接利用
    python条件表达式来执行简单快捷的代码

28:rf中执行python表达式

    *** Test Cases ***
    1:evaluate+python表达式,最终生成一个值,false还是true都是有个标准,
    2:条件表达式还可以计算一个值,比如列表生成式:run keyword if  [i for i in range(10)]   log to console    条件成立    ,这个列表不为空,肯定是成立的    
    3:获取条件表达式的结果:evaluate可以获取条件表达式的结果    ${res}  evaluate  [i for i in range(10)]     
        #获取条件表达式的结果存到res变量,res=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
        evaluate的底层逻辑实现其实是python的eval函数,eval函数接收一个字符串,字符串的内容是python条件表达式都可以去执行返回一个结果
        
        
    表达式1
            run keyword if  ''   log to console    条件成立     #如果条件成立,会打印,但是这里空字符串条件为假,没有成立
            run keyword if  None   log to console    条件成立   #也没有打印
            run keyword if  []   log to console    条件成立     #空列表也没有打印
            run keyword if  0   log to console    条件成立     #0也没有打印
            run keyword if  1   log to console    条件成立     #1,条件成立打印了,只有这个才为真,其余四个都是false
                
        rf条件表达式除了true和false以外是这种类型的空字符串,空列表,none和0都是false的,字符串里面带空格为真
            
            run keyword if  [i for i in range(10)]   log to console    条件成立        
    

    
    表达式2:evaluate获取表达式的值
            #条件表达式的结果(一行python语句能够解决的可以这么写,比如找0-9的偶数啊什么的可以使用evaluate实现)
            ${res}  evaluate  [i for i in range(10)]
            log to console  ${res}    
            
            evaluate  print("hello world")      #这样evaluate里面写python语句也是可以的,执行的结果会体现再log日志上
        
        {'a': '1', 'b': '2', 'c': '3'}["c"]=123        #这个表达式再python里面能够执行,但是再python的eval函数里面执行不了,因为eval函数不支持赋值操作
        
        ${dict}     create dictionary  a=1  b=2  c=3            #创建一个字典
        log to console  ${dict}
    #    evaluate   $dict["c"]=123                               #修改字典的值,python里面可行,rf里面不可行,eval函数不支持赋值操作
        evaluate    $dict.update({"c":123})                     # dict.update[{"c":123}]     把字典{"c":123} 里面的数据更新导dict里面
        log to console  ${dict}        
        
        evaluate    $dict.update({"c":123})        这样来修改字典里面c键的值,这样写---注意点
        
            evaluate函数不支持赋值操作,不建议用evaluate运行复杂的代码,复杂的代码交给python函数来实现

29:rf里面的初始化和清除

  进入测试用例之前所要做的事情setup,一般准备用例所需要的数据
  清除是这个用例会生成一些测试数据,把垃圾数据删除,防止对其他用例产生干扰 teardown
  初始化和清除都是分级别的,,testcase测试用例级别,testsuit套件级别
  pytest的初始化和清除是:functon函数 class类 module模块 package包 session -----文件层级,function类似用例级别
  rf就两种文件:一种测试用例,一种测试套件(文件+目录)

    一:rf用例级别的初始化和清除        testcase里面定义[setup]或者[teardown]  后面跟一个关键字,初始化和清除后面只能跟一个关键字,目前
        *** Test Cases ***
        case1
            [setup]  log to console  执行用例初始化
            [teardown]  log to console  执行用例清除
            log to console  执行用例主体部分

        case2
            [setup]  log to console  执行用例初始化
            [teardown]  log to console  执行用例清除
            log to console  执行用例主体部分

        case3
            log to console  执行用例主体部分
            
        用例1和用例2和用例3的清除都是自己独有的
    二:套件准备用例的公用环境:比如准备一套用户名和密码,套件级别的初始化和清除,放在*** Settings ***配置表里面的
        Suite Teardown和Suite Setup  进入和退出这个suite执行用例前后必须执行且只执行一次(套件级别的,这个套件这个模块只执行一次,不管里面有多少测试用例)    
            真正意义上的套件级别的初始化和清除
        Test Setup和Test Teardown    如果suite内的用例本身没有Setup和Teardown才会执行,用例级别的

            *** Settings ***
            Suite Teardown   log to console   执行套件清除
            Suite Setup     log to console   执行套件初始化
            Test Setup      log to console   执行全局测试用例初始化
            Test Teardown   log to console   执行全局测试用例清除
    三:套件目录级别的初始化和清除,目录也是套件(套件文件级别的初始化和清除二。卸载settings表里面就行)
        目录里面写个配置文件,手打 :__init__.robot 配置文件
    suite                                文件夹    
        __init__.robot                                robot文件配置文件,init里面定义了就行,用例里面不需要设置去调用,默认状态autouse自动执行的,默认执行,
            *** Settings ***                                #pytst初始化清除需要设置autouse或者去usefixture调用一下
            #套件目录级别的初始化和清除
            Suite Teardown   log to console   执行套件清除
            Suite Setup     log to console   执行套件初始化        
            
    
        用例的初始化和清除.robot        用例文件
            *** Settings ***
            Suite Teardown   log to console   执行套件清除
            Suite Setup     log to console   执行套件初始化
            Test Setup      log to console   执行全局测试用例初始化
            Test Teardown   log to console   执行全局测试用例清除

            *** Test Cases ***
            case1
                [setup]  log to console  执行用例初始化
                [teardown]  log to console  执行用例清除
                log to console  执行用例主体部分

            case2
                [setup]  log to console  执行用例初始化
                [teardown]  log to console  执行用例清除
                log to console  执行用例主体部分

            case3
                log to console  执行用例主体部分

    cmd里面执行:C:\Users\ywt\Desktop\RF_study>robot suite    
    四:假如用例2用例3的初始化和清除一样,两种方案
        1:再testcase里面写上两个一模一样的 [setup]和 [teardown]  初始化和清除        耦合多,不得行,后面不好改
        2:在套件文件的*** Settings ***里面定义Test Setup  和Test Teardown (初始化和清除) --如果用例有自身的setup和teardown那么用自身的
            如果用例没有setup和teardown那么用套件全局的Test Setup和Test Teardown ---默认提供的全局
        3:Test Setup和Test Teardown除了可以定义在套件文件的setting表里面也可也定义在init文件的setting表里面--目录级别里面定义
            初始化和清除遵循就近原则:假如 用例里面定义了[setup]和 [teardown]、
                                     套件文件定义了Test Setup  和Test Teardown 
                                     套件配置文件init(套件目录级别)也定义了Test Setup  和Test Teardown 
                                     那么会执行测试用例本身的[setup]和 [teardown]、
                                     如果测试用例没有定义初始化和清除执行套件文件的Test Setup  和Test Teardown 
                                     如果测试用例和套件文件都没有定义初始化和清除那么执行套件目录的Test Setup  和Test Teardown 
                                     
            用例有执行用例的,用例没有执行套件文件的,套件文件和用例都没有执行套件目录的-----就近原则
                                     

    rf的初始化和清除和pytest的区别:fixture实现,初始化和清除写在一个函数里面,rf是分开的
    五:rf的初始化和清除后面只能用一个关键字来表示
        用例也好,套件也好,如果初始化和清除比较复杂,不可能只使用一个关键字,那么就需要用到run keywords这个rf关键字
        run keywords  关键字,可以在一个逻辑里面运行多个关键字,rf一个逻辑行使用多个关键字,如下:
        
        run keywords +关键字1   AND  +关键字2         这样的格式
        
        *** Settings ***
        Suite Teardown   log to console   执行套件清除
        Suite Setup     run keywords    log to console   执行套件初始化1
                        ...  AND  log to console   执行套件初始化2
  上面就是rf的初始化和清除,很弱,东西不多

30:创建一个测试套件文件:

  用例1:python开发一个测试库,course_mgr.py改库有一个listCourse可以返回教官系统所有的课程
    2:用rf测试用例来使用listcourse关键字,返回课程列表所有的课程文件名输出导日志文件中(使用循环)
  用例2:登录https://www.vmall.com/
      获取所有的热销榜单品的名称,打印在log报表中
      seleniumlibary和python的selenium都可以做

作业2
    course_mgr.py
        import requests
        def login_lesson(username,password):
            payload={
                    "username":username,
                    "password":password}
            header = {"Content-Typ": "application/x-www-form-urlencoded"}
            reps=requests.post("http://localhost/api/mgr/loginReq",data=payload, headers=header)
            reps.encoding = 'unicode_escape'
            return reps.cookies

        def listCourse(cookie):
            resp=requests.get('http://localhost/api/mgr/sq_mgr/?action=list_course&pagenum=1&pagesize=20',cookies=cookie)
            return resp.json()

        if __name__ == '__main__':
            print(listCourse())

    task.robot    
        *** Settings ***
        Library  course_mgr.py      #自定义库,
        Library  Collections        #标准
        Library  SeleniumLibrary    #第三方库

        *** Test Cases ***
        case1
            ${cookie}  login_lesson  auto  sdfsdfsdf
            ${res}  listCourse  ${cookie}
            ${courses}   evaluate  $res['retlist']
            ${list}  create list                        #创建一个新列表
            FOR  ${course}  IN   @{courses}              #@解包
            log  ${course}[name]                        #写在大括号外面不需要加引号,可以这么理解,括号里面python处理,括号外面rf处理
            append to list   ${list}  ${course}[name]   #append to list列表里面加元素,第一个参数是列表,第二个参数是追加的元素
            END
            should be true  $list==['python','selenium']

        case2
            open browser  https://www.vmall.com/  chrome
            set selenium implicit wait  10
            ${goods}    get webelements   css=div[class="span-968 fl"] li div[class="grid-title"]       #${goods}是一个元素列表
            FOR  ${good}  IN  @{goods}
            log to console    ${good.text}          #${good.text}获取元素文本。里面是python处理的,加外面是rf处理的
            ${text}  get text  ${good}              #get text 可以使用元素获取文本,也可以使用
            log to console  ${text}                 #{}大括号,rf会以python语法去计算大括号内的表达式--表达式是变量就是变量,变量调用了某个属性那么最终取得是属性
            END
            log to console  ${goods}
            close browser
            
            
    *** Settings ***
    Library  course_mgr.py      #自定义库,            路径导入法
    Library  course_mgr              #自定义库,        这是模块导入法:和python模块一样,遵循python导入原则pythonpath模块搜索路径
    Library  Collections        #标准
    Library  SeleniumLibrary    #第三方库
    如上,自定义库得导入使用了.py但是标准库和第三方库都没有使用.py导入,
    
    rf也是python'程序搜索模块路径也是python得,通过pythonpath路径寻找,
    默认的PYTHONPATH都是下面这些,都是python下面的一些目录,course_mgr.py没在下面的目录下,这时候有两种方法
    1:添加course_mgr.py库的路径到pythonpath:-P  
        C:\Users\ywt\Desktop\RF_study>robot -P 作业2 作业2                        :指定C:\Users\ywt\Desktop\RF_study\作业2        到pythonpath
        原理将库文件所在的目录添加到pythonpath里面
        
    2:以项目文件为pythonpath的起点RF_study这个项目就是pythonpath的起点        
        那么每个测试套件的*** Settings ***导入自定义库的时候以C:\Users\ywt\Desktop\RF_study这个路径为起点找自定义库文件course_mgr.py
        *** Settings ***
        Library  作业2.course_mgr                        #导入的方法和python一样  xxx.xxx.xxx
            settings表里面这样导入就可以了,
            
        cmd执行命令的时候:C:\Users\ywt\Desktop\RF_study>robot -P . 作业2        
            -P .  执行当前路径为pythonpath执行即可
    
    PYTHONPATH:
      C:\Users\ywt\Desktop\RF_study
      E:\Program Files\Python36\Scripts\robot.exe
      e:\program files\python36\python36.zip
      e:\program files\python36\DLLs
      e:\program files\python36\lib
      e:\program files\python36
      e:\program files\python36\lib\site-packages
      e:\program files\python36\lib\site-packages\win32
      e:\program files\python36\lib\site-packages\win32\lib
      e:\program files\python36\lib\site-packages\Pythonwin

    导入文件的标准就是以项目根目录写相对路径,再cmd运行时候指定  -P .  以项目根目录添加到pythonpath里面----原则

31:用户关键字与资源文件

  作业3:
    case1:验证当前系统没有课程能否添加一个课程
      前置条件:没有课程
      测试步骤:添加课程,输入课程名,详情描述,展示次序,点击创建
      预期结果:创建课程正确显示再下面的课程列表中

      初始化和清除操作:当前系统没有课程状态,删除所有的课程,

      1:开发一个关键字deletealllesson清除所有的课程

      使用rf,seleniumlibary和python selenium使用的浏览器不是同一个,不要混淆
        代码里面的浏览器是driver=webdriver.Chrome()这行代码打开的
        rf里面是通过open browser打开的浏览器,和python里面的不是打开的同一个浏览器
          所以rf里面新打开的浏览器需要重新登录
          大多数场景都是python实现关键字rf引用

      rf中三个重要关键字后面+python语法的
        1:should be true 断言的
        2:run keyword if 流程控制的
        3:evaluate 计算python表达式的值给一个变量,值不接受也可以

   代码一阶段:如下

作业3
    courselib.py
        from selenium import webdriver
        import time
        def deleteAllLesson():
            driver=webdriver.Chrome()               #定义测试库所有的表达式写在函数里面,不写函数里面,写外面导入库就会执行代码
                                                    #没有调用任何函数就会生成浏览器,代码不得行
            driver.get('http://localhost/mgr/login/login.html')
            driver.implicitly_wait(10)
            driver.find_element_by_id('username').clear()
            driver.find_element_by_id('username').send_keys('auto')
            driver.find_element_by_id('password').clear()
            driver.find_element_by_id('password').send_keys('sdfsdfsdf')
            driver.find_element_by_tag_name('button').click()
            #删除所有课程操作
            eles=driver.find_elements_by_css_selector('.tablesorter tbody tr')
            for i in range(len(eles)):
                driver.find_element_by_css_selector('.tablesorter tbody tr button[ng-click="delOne(one)"]').click()
                driver.find_element_by_css_selector('.bootstrap-dialog-footer-buttons button:nth-child(2)').click()
                time.sleep(1)           #确认后马上删除下一个可能会出问题,等待弹出框消失
            driver.quit()

        if __name__ == '__main__':
            deleteAllLesson()
    
    作业3.robot                
        *** Settings ***
        Library  courselib.py
        Library  SeleniumLibrary
        Library  Collections        #标准


        *** Test Cases ***
        添加课程1
            [Setup]   deleteAllLesson
            [Teardown]   deleteAllLesson
            open browser  http://localhost/mgr/login/login.html  chrome
            set selenium implicit wait  10
            #输入用户名和密码
            clear element text  id=username
            input text  id=username    auto
            clear element text  id=password
            input text  id=password    sdfsdfsdf
        #    click element          #操作任意类型的元素
        #    click button            #只能操作属于button元素的类型
            click element  css=[type="button"]
            #添加课程
            click element  css=[ng-click="showAddOne=true"]
            input text  css=[ng-model="addData.name"]   test001课程  #一般input操作input,textarea或者text这种可输入的文本框,会找目标元素
            input text  css=[ng-model="addData.desc"]   自动化测试框架     #输入详情描述
            input text  css=[ng-model="addData.display_idx"]  1         #输入展示次序
            click element  css=[ng-click="addOne()"]                    #点击创建

            #检查课程是否符合预期(当前课程列表里面课程名)
            ${courses}  get webelements  css=tr td:nth-child(2)             #课程元素列表

        #    #获取列表中元素文本,循环添加到一个列表,这么些有点麻烦,需要创建列表还需要for循环添加,直接使用列表生成式evaluate
        #    ${list}  create list
        #    FOR  ${i}  IN   @{courses}
        #        append to list   ${list}  ${i.text}         #循环加元素
        #    END
            ${list}  evaluate  [i.text for i in $courses]       #一行代码能够解决的可以使用evaluate
            #校验,注意空格不要敲多个
            should be true   'test001课程' in $list           #python条件表达式 in需要小写
            close browser

        添加课程2
            [Setup]   deleteAllLesson
            [Teardown]   deleteAllLesson
            open browser  http://localhost/mgr/login/login.html  chrome
            set selenium implicit wait  10
            #输入用户名和密码
            clear element text  id=username
            input text  id=username    auto
            clear element text  id=password
            input text  id=password    sdfsdfsdf
            click element  css=[type="button"]
            click element  css=[ng-click="showAddOne=true"]
            input text  css=[ng-model="addData.name"]   test002课程
            input text  css=[ng-model="addData.desc"]   自动化测试框架
            input text  css=[ng-model="addData.display_idx"]  2
            click element  css=[ng-click="addOne()"]
            ${courses}  get webelements  css=tr td:nth-child(2)
            ${list}  evaluate  [i.text for i in $courses]
            #校验,注意空格不要敲多个
            should be true   'test002课程' in $list
            close browser

    测试套件里面两个测试用例,都是添加课程,知识添加课程的参数不同,初始化清除做好,可以一种循环跑

    代码一阶段:用例代码存在重复,可维护性变差,testcase逻辑都是一样--python里面是将相同逻辑做成一个函数,或者类
      所以rf里面也要做成函数,rf里面是以用户关键字存在的---用户关键字定义在用户关键字表里面:*** Keywords ***
代码第二版:如下

32:用户关键字学习:

用户关键字.robot
        #用户关键字定义在keywords表中
        *** Test Cases ***
        case1
            关键字1            #执行用户关键字里面定义好的代码,类似python的函数,封装相同逻辑--用户关键字
        case2
            带参关键字2  20201201
        case3
            带参关键字3-默认参数
        case4
            带参关键字4-多个参数  小强  小红  小紫
        case5
            带参关键字5-键值对参数  湖南=长沙  江苏=南京  广东=广州            
        case6
            ${res}  带参关键字6-返回值
            log to console  ${res}

        #Keywords 一般定义在测试用例表的下面,约定俗成的关系,规范
        *** Keywords ***
        关键字1                #用户关键字1
            log to console  步骤1
            log to console  步骤2
            log to console  步骤3
            log to console  步骤4
            log to console  步骤5

        带参关键字2
            #${date}        需要传这个参数
            [Arguments]   ${date}               #Arguments这里定义形参数
            log to console  Today is ${date}

        带参关键字3-默认参数
            [Arguments]  ${time}=12点            #time的默认值是12,现在有了默认值是非必传参数了
            log to console  ${time} 吃饭

        带参关键字4-多个参数             #多个参数就是传个数组
            [Arguments]  @{friends}      #rf里面@等同python里面的*,@表示多个参数和解包
            log to console  小明的小朋友有很多:${friends}            #现在把多个参数封装成一个列表打印,log to console  只能接收一个参数
            log many  @{friends}
            log many  ${friends}

        带参关键字5-键值对参数
            [Arguments]  &{citys}                    #与符号,等同python的**
            FOR  ${city}  IN  &{citys}                  #city是一个键值对,有的rf版本&{citys}这样解包不了,需要@{citys}才能解包
                                                        #FOR  ${city}  IN  @{citys}         ${city}取的是key的值:湖南
                                                        #FOR  ${city}  IN  &{citys}         ${city}取得是一个列表:('湖南', '长沙')
            log to console  省与省会:${city}[0] ${city}[1]
            END

            FOR  ${city}  IN  @{citys}                                      #使用@{citys}   这样循环取值也可以
            log to console  省与省会:${city} ${citys}[${city}]              #rf版本不同有差异,取字典写法:${citys}[${city}]
            END    

        带参关键字6-返回值
            ${score}  get value from user  请输入您的分数>>>
            ${res}  set variable if  int($score)>=60  通过  ELSE  不通过
            [Return]  ${res}                    #返回值Return    

    如上在套件文件的*** Keywords ***里面定义关键字只能这个套件文件使用,如果关键字想其他套件都能使用的话,共享关键字的话
        python通过lib库文件的机制,被其他模块导入,rf也可也的---资源文件
        python库也是python文件,当成这个lib库文件去导入
        
        导入方式是:Resource  +资源文件的文件名
        
        rf文件作为资源文件被其他rf当成库来导入,那么资源文件里面不能包含测试用例列表,如果包含测试用例列表,rf认为是测试套件,不能被其他测试库导入
            所以需要创建一个额外的测试库--比如rclib.robot--如下:创建一个专门的资源文件,没有测试用例的才能被其他文件当作模块导入
    
    rclib.robot                    #rf资源文件不能包含测试用例包,否则会被当成测试套件而不能当成库导入
        *** Keywords ***
        #封装的时候力度把控,看当前和今后的需求,全部流程封装成一个关键字还是登录封装成一个关键字,检查封装成一个关键字--根据行为封装,独立开来

        打开浏览器并且登录
            [Arguments]  ${use}=auto  ${psw}=sdfsdfsdf
            open browser  http://localhost/mgr/login/login.html  chrome
            set selenium implicit wait  10
            clear element text  id=username
            input text  id=username    ${use}
            clear element text  id=password
            input text  id=password    ${psw}
            click element  css=[type="button"]

        添加课程
            [Arguments]  ${name}  ${name}  ${idx}
            click element  css=[ng-click="showAddOne=true"]
            input text  css=[ng-model="addData.name"]   ${name}
            input text  css=[ng-model="addData.desc"]   ${name}
            input text  css=[ng-model="addData.display_idx"]  ${idx}
            click element  css=[ng-click="addOne()"]

        检查课程
            [Arguments]  ${expect}
            ${courses}  get webelements  css=tr td:nth-child(2)
            ${list}  evaluate  [i.text for i in $courses]
            log to console  ${list}
            log to console  ${expect}
            should be true    $expect in $list

        关闭浏览器
            close browser    
            
    rf资源文件的一个作用
    

33:代码二阶段:作业结合用户关键字

作业3.robot                                        #定义用户关键字后的改良版本
            *** Settings ***
            Library  courselib.py
            Library  SeleniumLibrary
            Library  Collections        #标准

            *** Test Cases ***
            添加课程1
                [Setup]   deleteAllLesson
                [Teardown]   deleteAllLesson
                打开浏览器并且登录
                添加课程  appium课程  appium课程  1
                检查课程  appium课程
                关闭浏览器

            添加课程2
                [Setup]   deleteAllLesson
                [Teardown]   deleteAllLesson
                打开浏览器并且登录
                添加课程  selenium课程  selenium课程  2
                检查课程  selenium课程
                关闭浏览器


            *** Keywords ***
            #封装的时候力度把控,看当前和今后的需求,全部流程封装成一个关键字还是登录封装成一个关键字,检查封装成一个关键字--根据行为封装,独立开来

            打开浏览器并且登录
                [Arguments]  ${use}=auto  ${psw}=sdfsdfsdf
                open browser  http://localhost/mgr/login/login.html  chrome
                set selenium implicit wait  10
                clear element text  id=username
                input text  id=username    ${use}
                clear element text  id=password
                input text  id=password    ${psw}
                click element  css=[type="button"]

            添加课程
                [Arguments]  ${name}  ${name}  ${idx}
                click element  css=[ng-click="showAddOne=true"]
                input text  css=[ng-model="addData.name"]   ${name}
                input text  css=[ng-model="addData.desc"]   ${name}
                input text  css=[ng-model="addData.display_idx"]  ${idx}
                click element  css=[ng-click="addOne()"]

            检查课程
                [Arguments]  ${expect}
                ${courses}  get webelements  css=tr td:nth-child(2)
                ${list}  evaluate  [i.text for i in $courses]
                log to console  ${list}
                log to console  ${expect}
                should be true    $expect in $list

            关闭浏览器
                close browser

34:代码三阶段:可以把用户关键字定义到外面的rclib.robot文件被所以的测试套件都能导入使用,按照方法二定义在测试用例里面的关键字只能本套件使用

35:RF数据驱动   [Template]

  也是建立在用户关键字的基础上的(重点) [Template]来做rf的数据驱动
  使用常用:比如接口用例,逻辑代码一样,只是参数不一样,几十条个接口参数组合不可能些几十条用例,---数据驱动,参数化,没有pytest强大
  1:首先需要定义一个用户关键字

    数据驱动.robot
        *** Test Cases ***
        case1
            api_test  接口测试参数1  接口测试参数2  接口测试参数3                     #正常这样调用的

        case2               #[Template]做数据驱动的用法--一个接口跑很多组数据,这样些还是属于一个case测试用例,没有pytest强大
                            #rf只能把参数写在用例主体部分,rf数据驱动不强,目前参数写死在用例文件里面,调用时候少写一步用户关键字而已
                            #
                            #[Template] 模板的意思
            [Template]  api_test        #模板后跟一个关键字,用例主体部分是模块关键字的参数
            接口测试参数1  接口测试参数2  接口测试参数3
            接口测试参数1  接口测试参数2  接口测试参数3
            error  接口测试参数2  接口测试参数3             #测试用例,如果测试用例执行失败,报错,不会影响下面的用例执行--这就是参数化的作用
                                                            #如果用for循环做数据驱动,一条执行不通过后面不会执行
            接口测试参数2  接口测试参数3                    #测试用例,如果传参异常,程序就gg了,后面的代码都不会执行了
            接口测试参数1  接口测试参数2  接口测试参数3

            log to console  测试结束了                   #这样写会报错。会把这个log to console  测试结束了当成参数--数据驱动有点弱
                                                        #关键字一般不设置带返回值,批量执行---

        case3               #for循环做数据驱动,运行到error第三条测试用例报错了,后续的参数不会执行了但是[Template]数据驱动也能执行
                            #
            FOR  ${api}  IN   参数1  参数2  error  参数4  参数5
            api_test  ${api}
            run keyword and continue on failure  api_test  ${api}           #数据驱动这样写,即使关键字失败了也继续,后续也能执行
                                                                            #for循环实现参数化可以这样写run keyword and continue on failure
                                                                            #但是没有[Template]清晰,还要写for循环
            run keyword and ignore error  api_test  ${api}                  #这样参数化也行,忽略错误--但是error也显示通过的,不得行
                                                                            #看不到报错提示,所以for循环参数化只能使用上面一个
            END
        #数据驱动原则之一:即使某组参数导致用例失败,也不影响后续参数的执行,同时测试结果中能够正确显示出参数的通过结果
            #通过能够显示,失败也能够显示
        *** Keywords ***
        api_test
            [Arguments]  ${args1}  ${args2}=None  ${args3}=None
            run keyword if  $args1=='error'  fail              #fatal error  停止所有测试执行,error模拟用例失败,
                                                                #如果第一个参数传error时候,那么这条用例失败了fail
            log to console  ${args1}
            log to console  ${args2}
            log to console  ${args3}

36:实现一个关键字:deleteAllLesson使用用户关键字来实现  定义在robot文件里

  上面代码的持续优化
    1:减少web窗口打开的次数
    2:用户名密码url等写死在代码中不好
      数据根据不同的测试环境,可能变化,如果这些数据都散落在各个测试脚本中,不利于统一修改
      一般这种类型的数据,写在一个配置文件里面里面的--便于维护
      做成公共变量,配置文件

  rf中的配置文件:

    1:*** Variables *** 通过Variables表,测试套件里定义的公共变量只能本套件使用
      如果需要所有的套件通用,需要定义一个commonvar.robot 公共变量表,或者一个资源文件里面定义关键字+公共变量表,
      不要在一个有测试用例的表里定义*** Variables ***公共变量和*** Keywords ***关键字,

      除非你的关键字和公共变量只在自己的套件使用可以定义在套件里面

    commonvar.robot    
        #资源文件,放公共变量
        *** Variables ***
        ${LoginUrl}  http://localhost/mgr/login/login.html
        ${StudentUrl}  http://localhost/mgr/ps/mgr/index.html#/student

        @{database}  127.0.0.1:3307    198.0.0.1:3307           #保存数据库的地址,多个数据,存个列表,列表类型

        &{use1}  name=auto  psw=sdfsdfsdf                       #字典类型,存用户名和密码
        &{use2}  name=auto2  psw=sdfsdfsdf
        &{use3}  name=auto3  psw=sdfsdfsdf
    rf公共变量.robot                    #其他套件导入资源文件使用
        *** Settings ***
        Resource  commonvar.robot               #引入资源文件,导入资源文件里面的公共变量,类似导入库,多个套件都可以使用公共变量了

        *** Test Cases ***
        case1
            log to console  ${LoginUrl}
            log to console  ${StudentUrl}
            log to console  ${database}[0]             #$整体形式
            log to console  ${database}[1]             #$整体形式
            log many  @{database}                       #解包打印,log里面查看

        case2
            log to console  ${use1}[name]                   #${use1}建议使用这个,不提倡使用&{use1}会警告
            log to console  ${use1}[psw]

  rf定义变量的形式有限,
    1:字符串类型,整数等类型不太方便
    2:字典key value都是字符串类型的

 37:代码第四版:把变量抽取到资源文件里面:

    webui操作                                #测试套件文件夹
        __init__.robot                        #init先于测试用例执行的文件
            #存放套件目录初始化清除操作   打开浏览器和关闭浏览器
            *** Settings ***
            Resource  task4.robot
            Suite Setup  打开浏览器
            Suite Teardown  关闭浏览器
        
        新增课程.robot                        #测试用例文件
            *** Settings ***
            Resource  task4.robot
            Suite Setup  登录动作

            *** Test Cases ***
            添加课程1
                [Setup]   删除所有课程
                [Teardown]   删除所有课程
                添加课程  appium课程  appium课程  1
                检查课程  appium课程

            添加课程2
                [Setup]   删除所有课程
                [Teardown]   删除所有课程
                添加课程  selenium课程  selenium课程  2
                检查课程  selenium课程
                
    task4.robot                                #资源文件,类似python的库文件,这个资源文件不能有testcase表,否则别的模块导入失败
        *** Settings ***
        Library  SeleniumLibrary
        Library  Collections

        *** Variables ***
        #资源文件里面定义全局变量
        #1:用户名+url关键字
        ${LoginUrl}  http://localhost/mgr/login/login.html              #url
        &{use1}     name=auto  psw=sdfsdfsdf                            #用户名

        *** Keywords ***
        #封装的时候力度把控,看当前和今后的需求,全部流程封装成一个关键字还是登录封装成一个关键字,检查封装成一个关键字--根据行为封装,独立开来

        登录动作
            [Arguments]  ${use}=${use1}[name]  ${psw}=${use1}[psw]
            #在打开浏览器的情况下进入登录页面,浏览器已经打开了,selenium里面使用get方法--rf使用go to  +url
            go to  ${LoginUrl}
            clear element text  id=username
            input text  id=username    ${use}
            clear element text  id=password
            input text  id=password    ${psw}
            click element  css=[type="button"]
 
        添加课程
            [Arguments]  ${name}  ${name}  ${idx}
            click element  css=[ng-click="showAddOne=true"]
            input text  css=[ng-model="addData.name"]   ${name}
            input text  css=[ng-model="addData.desc"]   ${name}
            input text  css=[ng-model="addData.display_idx"]  ${idx}
            click element  css=[ng-click="addOne()"]

        检查课程
            [Arguments]  ${expect}
            ${courses}  get webelements  css=tr td:nth-child(2)
            ${list}  evaluate  [i.text for i in $courses]
            log to console  ${list}
            log to console  ${expect}
            should be true    $expect in $list


        删除所有课程             #删除所有课程,python代码全改成rf:SeleniumLibrary的关键字
            #2:删除所有的课程
            set selenium implicit wait  2
            FOR  ${one}  IN RANGE  9999
            ${del_btns}  get webelements  css=.tablesorter tbody tr button[ng-click="delOne(one)"]
            exit for loop if  $del_btns==[]                  #类似break
            click element  ${del_btns}[0]                       #rf取列表元素删除
            click element  css=.bootstrap-dialog-footer-buttons button:nth-child(2)
            sleep  1
            END

        打开浏览器
            open browser  http://localhost  chrome      #随便提供一个url,为了给关键字打开一个页面,改页面不一定要求能访问成功
                                                                #只要格式正确即可
            set selenium implicit wait  10
        关闭浏览器
            close browser

  需要一种方法灵活定义公共变量类型:  python可以实现要求,引入python文件作为公共变量,更好,
    python模块文件提供给rf使用,config.py 配置文件

38:代码第五版

webui操作            
                __init__.robot            文件和上面代码4版相同
                新增课程.robot            文件和上面代码4版相同
            
            config.py
                LoginUrl='http://localhost/mgr/login/login.html'
                use1={'name':'auto','psw':'sdfsdfsdf'}

            task4.robot
                *** Settings ***
                Library  SeleniumLibrary
                Library  Collections
                Variables   config.py                        #这里导入python资源配置文件config.py,遵循python导入原则,需要这样导入变量配置文件,后面引用就可以

                *** Keywords ***
                #封装的时候力度把控,看当前和今后的需求,全部流程封装成一个关键字还是登录封装成一个关键字,检查封装成一个关键字--根据行为封装,独立开来

                登录动作
                    [Arguments]  ${use}=${use1}[name]  ${psw}=${use1}[psw]
                    #在打开浏览器的情况下进入登录页面,浏览器已经打开了,selenium里面使用get方法--rf使用go to  +url
                    go to  ${LoginUrl}
                    clear element text  id=username
                    input text  id=username    ${use}
                    clear element text  id=password
                    input text  id=password    ${psw}
                    click element  css=[type="button"]

                添加课程
                    [Arguments]  ${name}  ${name}  ${idx}
                    click element  css=[ng-click="showAddOne=true"]
                    input text  css=[ng-model="addData.name"]   ${name}
                    input text  css=[ng-model="addData.desc"]   ${name}
                    input text  css=[ng-model="addData.display_idx"]  ${idx}
                    click element  css=[ng-click="addOne()"]

                检查课程
                    [Arguments]  ${expect}
                    ${courses}  get webelements  css=tr td:nth-child(2)
                    ${list}  evaluate  [i.text for i in $courses]
                    log to console  ${list}
                    log to console  ${expect}
                    should be true    $expect in $list


                删除所有课程             #删除所有课程,python代码全改成rf:SeleniumLibrary的关键字
                    #2:删除所有的课程
                    set selenium implicit wait  2
                    FOR  ${one}  IN RANGE  9999
                    ${del_btns}  get webelements  css=.tablesorter tbody tr button[ng-click="delOne(one)"]
                    exit for loop if  $del_btns==[]                  #类似break
                    click element  ${del_btns}[0]                       #rf取列表元素删除
                    click element  css=.bootstrap-dialog-footer-buttons button:nth-child(2)
                    sleep  1 
                    END

                打开浏览器
                    open browser  http://localhost  chrome      #随便提供一个url,为了给关键字打开一个页面,改页面不一定要求能访问成功
                                                                        #只要格式正确即可
                    set selenium implicit wait  10
                关闭浏览器
                    close browser

39:场景:多个测试环境

  #测试环境1 192.168.10.1
  #测试环境2 192.168.10.2

  测试环境修改需要改配置文件,修改配置文件可能改错,用户名和密码可能不匹配,环境1的用户名和密码写到2
  配置项目很多,几十个,一个个改很麻烦---最好配置文件每个环境单独保留一个配置文件,这样不需要去修改

            config1.py
                LoginUrl='http://192.168.10.1/mgr/login/login.html'
                use1={'name':'auto111','psw':'sdfsdfsdf111'}
            
            config2.py
                LoginUrl='http://192.168.10.2/mgr/login/login.html'
                use1={'name':'auto222','psw':'sdfsdfsdf222'}
                
            公共变量2.robot                                #这里使用公共变量LoginUrl和use1都不需要导入,直接cmd执行的时候指定-V config1.py  公共变量文件就行
                *** Test Cases ***
                case1
                    log to console  ${LoginUrl}             #打印 http://192.168.10.1/mgr/login/login.html
                    log to console  ${use1}                 #打印 {'name': 'auto123', 'psw': 'sdfsdfsdf123'}
                    
                    
        rf执行的时候可以执行的时候通过一个参数指定变量文件    -Variables或者-V
            套件不需要引用变量文件,直接cmd执行的时候引用。如下 
            
        C:\Users\ywt\Desktop\RF_study\作业4>robot -V config2.py -P . 公共变量2.robot
            
             -V config2.py            -V指定变量文件的相对路径,为当前路径RF_study下的config2.py文件夹就是变量配置文件--这样修改测试环境就很方便
        
                C:\Users\ywt\Desktop\RF_study\作业4>robot -V config1.py -P . 公共变量2.robot            #指定变量1的配置文件
                C:\Users\ywt\Desktop\RF_study\作业4>robot -V config2.py -P . 公共变量2.robot            #指定变量2的配置文件
                
                这样使用前提变量文件存在,
                
                所有相对路径参考命令行的当前目录,命令行当前目录调到项目根目录,以这样为准则
                    项目根目录为相对目录的一个起点
    
    这就是rf项目公共变量的使用
        1:通过rf提供的*** Variables ***关键字表提供一个共享变量的使用,这种写法不太自由
        2:通过python文件来定义--更适合工作环境的使用---py文件最合适=--
            rf可以直接导入python文件的公共变量--
            
        库是库,库定义函数或者类
        配置文件:定义常量就行了

40:python扩展关键字

  1:python关键字大小写不敏感
  2:函数名中的下划线可以用空格来代替
  3:可以有多个空格,支离破碎都行,但是空格只能间隔一个空格,--建议不这么写,
  4:函数名称以下划线开头不会作为关键字,如果带了下划线会寻找库中名称最相近的函数作为替代品,默认去了下划线找替代品,差了比较多找不到
    函数名称以下划线结尾没所谓,没什么意义

  如下:

        ywtlib.py
            def return_list():
                return [i for i in range(10)]
            
            def _return_list():                     #下划线开头的函数
                return [i for i in range(10,20)]

        python库关键字扩展.robot
            *** Settings ***
            Library  ywtlib.py

            *** Test Cases ***
            case1
                ${res}  Return List                     #return_list = return list = Return List  = Re turn Li st
                                                        #四种关键字写法都可以,都是引用的ywtlib.py库里面的return_list函数
                log to console  ${res}                #[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

            case2
                ${res}  _return_list
                log to console  ${res}                #还是输出[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],第一个return_list关键字的数据,写法的bug,
                                                         _return_list被当成return_list第一个关键字了,
                                                         如果ywtlib.py库里面的return_list函数删除,case2执行报错,找不到_return_list关键字

41:rf python类作为关键字

  如果关键字函数定义在类中,导入的时候需要用   模块.类名   方法导入  模块法
    类里面的关键字需要连同类一起导入,
    模块导入发执行的时候一定记得指定pythonpath--
    -P P=PYTHONPATH
    . .=当前路径 

  1:类里面的函数下划线开头也是不识别的,和模块导入一样
  2:库的class类名和文件名同名,可以不用写后面的class
  3:如果只需要导入模块这一层可以使用相对路径   Library keywordslib.py
    模块这一层文件名是路径    pylib/testlib.py    这是路径导入发,文件后缀名.py一定要写,不写就会报错
  4:路径法使用   /作为分隔符    如果使用路径发需要加文件后缀.py   Library pylib/testlib.py
     模块法是以    .作为分隔符    如果使用路径法不需要加文件后缀    Library pylib.testlib

    在settings表中 声明资源文件和变量文件
      路径:目录之间的分隔符号不用点 . ,而是使用斜杠 /
    在settings表中声明 测试库 :
      路径,目录之间的分隔符用点 . 也可以使用斜杠 /
      路径分隔符用点 .后面不加 .py ,用斜杠/后面加 .py

  5:python 库引入了其他模块,
    库文件中导入其他模块文件,相对路径也要从项目根目录开始
    确保导入的模块路径和rf导入的路径起始路径统一

  python库中的class存在继承
    确导入的模块路径和rf导入的路径起始路径统一
    使用的时候rf只需要导入子类即可

  5:rf文件导入python模块的函数,这个被导入的python模块还导入了其他模块  这时候记住一个原则
    1:以项目根目录为pythonpath寻找路径
    2:后面.py文件里面导入变量,函数,robot文件libary导入关键字还是导入rf公共变量,都以项目根目录为pythonpath导入起点写相对导入路径
      执行的时候指定 -P . 当前项目的根目录为pythonpath执行就可以了,

  5:如果类名和模块名相同,最好不要在外面独立定义函数,否则导入异常
    Library ywtlib.ywtlib   模块名和类名相同导入ok
    Library pylib/ywtlib.py  等同Library ywtlib.ywtlib
    Library pylib.ywtlib   库的class和文件名同名,可以不用写后面的class,不用指定类名,这样写就ok

    上面三种写法都可以导入模块名和类名相同的的情况下模块里面的ywtlib   类,库的class和文件名同名,可以不用写后面的class,三种写法都可以

    类名和模块名相同:导入模块需要使用:Library ywtlib     ywtlib模块里面还一个ywtlib类,
      但是Library ywtlib这样导入默认只导入ywtlib模块里面的ywtlib类,ywtlib模块里面其他函数导入不了,会产生bug  rf的一个bug
      类以外的都获取不到了

   定义和.py模块文件同名类的话外面不要独立定义函数了----坑 重点

  6:自定义关键字不要同名,导入两个不同模块的关键字同名或者导入了一个模块里面两个类里面两个函数名相同的模块
    测试套件使用这个同名的关键字就会报错 

  7:类里面存在init初始化函数
    导入类作为一个库会执行类里面的初始化方法,如果类里面初始化方法有参数
    关键字类有init初始化方法,需要传参数的时候,rf导入的时候进行类的传参

    Library pylib.ywtlib  localhost  8888      #从pylib模块的ywtlib模块导入ywtlib类,类的初始化需要两个参数localhost和8888
                             这样定义后后面就可以之间使用类里面的函数关键字 

  8:类里面的继承的情况
    子类继承父类导入库的时候,不用考虑太多,只需要导入子类即可
    同样相对目录以项目根目录为起点

   python类作为关键字代码实例

        keywordslib.py                        #关键字库都是函数
            def return_list():
                return [i for i in range(10)]

            def _return_list():             #下划线开头的函数
                return [i for i in range(10,20)]

            if __name__ == '__main__':
                print(return_list())
        
        ywtlib.py                            #关键字库都是
            import time
            class  SubLibrary:
                def  get_list(self):
                    return [1,2,3]

                def  get_dict(self):
                    return {"a":1,"b":2}

            class ywtlib:
                def  current_time(self):
                    dt=time.localtime()
                    t=time.strftime('%Y-%m-%d %H:%M:%S',dt)
                    return t

        python库关键字扩展.robot                #testcase--测试套件文件
            *** Settings ***
            Library  keywordslib.py
            Library  ywtlib.SubLibrary
            #Library  ywtlib.ywtlib              #同名模块导入ok
            Library  ywtlib                      #库的class和文件名同名,可以不用写后面的class,不用指定类名,这样写就ok


            *** Test Cases ***
            case1
                ${res}  return_list
                log to console  ${res}              #[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

            case2
                ${res}  get_list
                log to console  ${res}               #打印[1,2,3]
                ${res1}  get_dict
                log to console  ${res1}              #打印{'a': 1, 'b': 2}

            case3
                ${res}  current_time
                log to console  ${res}

  rf导入python库的关键字,python库还导入了其他文件的代码实例:

    ywtprj                        #项目文件,项目根目录 
        pylib                        #存放关键字库文件的
            conf6.py                #存放配置文件
                LoginUrl = 'http://localhost/mgr/login/login.html'
                use1 = {'name': 'auto', 'psw': 'sdfsdfsdf'}
            
            keywordslib.py            #存放函数类型的关键字
                def return_list():
                    return [i for i in range(10)]

                def _return_list():             #下划线开头的函数
                    return [i for i in range(10,20)]

                if __name__ == '__main__':
                    print(return_list())

            other.py                #存放函数类型的关键字,并且这个库还导入了配置文件里面的变量
                from pylib.conf6 import use1
                def get_user():
                    return use1['name']
            
            ywtlib.py                #存放类的关键字
                import time

                class  SubLibrary:
                    def  get_list(self):
                        return [1,2,3]

                    def  get_dict(self):
                        return {"a":1,"b":2}

                class ywtlib:
                    def  current_time(self):
                        dt=time.localtime()
                        t=time.strftime('%Y-%m-%d %H:%M:%S',dt)
                        return t
        
        python库关键字扩展.robot                    #测试套件文件
            *** Settings ***
            Library  pylib/keywordslib.py
            Library  pylib.ywtlib.SubLibrary
            #Library  ywtlib.ywtlib              #同名模块导入ok
            #Library  pylib/ywtlib.py            #等同Library  ywtlib.ywtlib
            Library  pylib.ywtlib                      #库的class和文件名同名,可以不用写后面的class,不用指定类名,这样写就ok
            Library  pylib.other

            *** Test Cases ***
            case1
                ${res}  return_list
                log to console  ${res}              #[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

            case2
                ${res}  get_list
                log to console  ${res}               #打印[1,2,3]
                ${res1}  get_dict
                log to console  ${res1}              #打印{'a': 1, 'b': 2}

            case3
                ${res}  current_time
                log to console  ${res}

            case4
                ${res}  get_user
                log to console  ${res}

  python库类里面的继承的情况代码实例

ywtprj
    pylib
        base.py                        #基类文件
            from robot.api import logger

            class  Father():
                def __init__(self):
                    logger.console('执行父类方法')

                def  make_money(self):
                    self.money=10000
                    return self             #为了实现链式调用如:  Father().make_money().show_money()     这样一次性调用多个类的函数

                def show_money(self):
                    logger.console(self.money)
                    return self.money
        
        conf6.py                        #配置文件
            LoginUrl = 'http://localhost/mgr/login/login.html'
            use1 = {'name': 'auto', 'psw': 'sdfsdfsdf'}    
            
        keywordslib.py
            def return_list():
                return [i for i in range(10)]

            def _return_list():             #下划线开头的函数
                return [i for i in range(10,20)]

            if __name__ == '__main__':
                print(return_list())    
        
        other.py
            from pylib.conf6 import use1    #库文件中导入其他模块文件,相对路径也要从项目根目录开始
            from pylib.base import Father

            def get_user():
                return use1['name']

            class  Child(Father):
                def use_money(self,cost=500):
                    self.money=self.money-int(cost)
                    return self             #加self为了实现链式调用如:Child().make_money().use_money().show_money()

            if __name__ == '__main__':
                Child().make_money().use_money().show_money()
                
        ywtlib.py
            import time

            class  SubLibrary:
                def  get_list(self):
                    return [1,2,3]

                def  get_dict(self):
                    return {"a":1,"b":2}

            from robot.api import logger
            class ywtlib:
                def __init__(self,host,port):
                    logger.console('执行初始化')
                    self.host=host
                    self.port=port


                def  current_time(self):
                    dt=time.localtime()
                    t=time.strftime('%Y-%m-%d %H:%M:%S',dt)
                    return t

                def  show_table(self):
                    logger.console(f'ip:{self.host} port:{self.port}')
    
    python库关键字扩展.robot
        *** Settings ***
        Library  pylib/keywordslib.py
        Library  pylib.ywtlib.SubLibrary
        #Library  ywtlib.ywtlib              #同名模块导入ok
        #Library  pylib/ywtlib.py            #等同Library  ywtlib.ywtlib
        Library  pylib.ywtlib  localhost  8888   #库的class和文件名同名,可以不用写后面的class,不用指定类名,这样写就ok
        Library  pylib.other
        Library  pylib.other.Child

        *** Test Cases ***
        case1
            ${res}  return_list
            log to console  ${res}              #[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

        case2
            ${res}  get_list
            log to console  ${res}               #打印[1,2,3]
            ${res1}  get_dict
            log to console  ${res1}              #打印{'a': 1, 'b': 2}

        case3
            ${res}  current_time
            log to console  ${res}

        case4
            ${res}  get_user
            log to console  ${res}

        case5
            show_table

        case6
            make_money
            use_money   5000
            use_money   4000
            show_money

42:testing

  新增一个用例2,初始化清除工作,包括关键字的实现在RF资源文件中实现

  我们要求两个用例放在不同的测试套件目录中:

    一个是‘课程管理’目录

    一个是‘老师管理’目录

  用例2:添加老师功能

    前置条件:系统中没有老师,已经有课程‘初中语文’ ,‘初中数学’

    step1:添加老师, 输入老师姓名、登录名、描述、展示次序为2,选择课程‘初中语文’

    预期结果:创建的老师正确显示在下面的表中。

    step 2:再添加 一个老师,展示次序为1, 选择课程‘初中数学’

    预期结果:创建的老师正确显示在下面的表中,井1按照展示次序排列。

  用例2的前置条件

    1:删除系统中所有的老师   测试用例的初始化

    2:删除系统中所有的课程

      为什么先删除老师再删除课程--老师是关联课程的,课程被老师依赖的,老师数据层级在上面,从上往下删除---先有课程再有老师

    3:添加课程 "初中数学""初中语文"

  自动化项目目录结构

    task        #项目根目录 

      pylib       #python库

      rflib       #资源文件

      tc        #测试用例

          老师管理 

          课程管理 

      cfg.py       #配置文件只有一个这么放

  代码开工:

    1:不管导入资源文件还是变量文件还是自定义python库文件,导入的相对路径都是以项目根目录为起点

    2:实现添加老师关键字+检查老师关键字

ywtprj                    #项目根目录 
    conf                #配置文件-文件夹
        conf.py
            LoginUrl='http://localhost/mgr/login/login.html'
            use1={'name':'auto','psw':'sdfsdfsdf'}
    
    pylib                #python封装的用户关键字
        testlib.py    
            from robot.libraries.BuiltIn import BuiltIn
            def test_kw():
                #需要通过rf调用该关键字才能正确执行
                BuiltIn().set_global_variable('${cookies}','asdasdadwdadad')
                #set_global_variable    设置rf全局变量

    rflib                #rf封装的rf关键字
        rc.robot
            *** Settings ***
            Library  SeleniumLibrary
            Library  Collections
            Variables   conf/conf.py

            *** Keywords ***
            #封装的时候力度把控,看当前和今后的需求,全部流程封装成一个关键字还是登录封装成一个关键字,检查封装成一个关键字--根据行为封装,独立开来

            登录动作
                [Arguments]  ${use}=${use1}[name]  ${psw}=${use1}[psw]
                #在打开浏览器的情况下进入登录页面,浏览器已经打开了,selenium里面使用get方法--rf使用go to  +url
                go to  ${LoginUrl}
                clear element text  id=username
                input text  id=username    ${use}
                clear element text  id=password
                input text  id=password    ${psw}
                click element  css=[type="button"]

            添加课程
                [Arguments]  ${name}  ${desc}  ${idx}
                click element   css=[ui-sref="course"]              #进入课程菜单
                click element  css=[ng-click="showAddOne=true"]
                input text  css=[ng-model="addData.name"]   ${name}
                input text  css=[ng-model="addData.desc"]   ${desc}
                input text  css=[ng-model="addData.display_idx"]  ${idx}
                click element  css=[ng-click="addOne()"]

            检查课程
                [Arguments]  ${expect}
                click element   css=[ui-sref="course"]              #进入课程菜单
                ${courses}  get webelements  css=tr td:nth-child(2)
                ${list}  evaluate  [i.text for i in $courses]
                log to console  ${list}
                log to console  ${expect}
                should be true    $expect in $list


            删除所有课程             #删除所有课程,python代码全改成rf:SeleniumLibrary的关键字
                click element   css=[ui-sref="course"]              #进入课程菜单
                #2:删除所有的课程
                set selenium implicit wait  2
                FOR  ${one}  IN RANGE  9999
                ${del_btns}  get webelements  css=.tablesorter tbody tr button[ng-click="delOne(one)"]
                exit for loop if  $del_btns==[]                  #类似break
                click element  ${del_btns}[0]                       #rf取列表元素删除
                click element  css=.bootstrap-dialog-footer-buttons button:nth-child(2)
                sleep  1
                END

            打开浏览器
                open browser  http://localhost  chrome      #随便提供一个url,为了给关键字打开一个页面,改页面不一定要求能访问成功
                                                                    #只要格式正确即可
                set selenium implicit wait  10
                set global variable  ${g_var}  hello            #设置一个全局变量可以其他套件${g_var}直接使用

            关闭浏览器
                close browser

            删除所有老师
                #2:删除所有的老师
                click element   css=[ui-sref="teacher"]               #进入老师菜单,go to也可以,但是go to可能会刷新页面
                set selenium implicit wait  2
                FOR  ${one}  IN RANGE  9999
                ${del_btns}  get webelements  css=[ng-click="delOne(one)"]
                exit for loop if  $del_btns==[]                  #类似break
                click element  ${del_btns}[0]                       #rf取列表元素删除
                click element  css=[class="btn btn-primary"]
                sleep  1
                END

            添加老师
                [Arguments]  ${realname}  ${username}  ${desc}  ${idx}  ${course}
                click element   css=[ui-sref="teacher"]                             #进入老师菜单,go to也可以,但是go to可能会刷新页面
                click element  css=[ng-click="showAddOne=true"]                     #点击添加老师按键
                input text  css=[ng-model="addEditData.realname"]   ${realname}     #输入老师姓名
                input text  css=[ng-model="addEditData.username"]   ${username}     #输入登录名
                input text  css=[ng-model="addEditData.desc"]  ${desc}              #输入描述
                input text  css=[ng-model="addEditData.display_idx"]  ${idx}        #输入展示次序
                select from list by label  css=[ng-model="$parent.courseSelected"]  ${course}       #select from list by label select标签根据label文本值选取元素
                click element  css=[class="fa fa-plus"]                             #关联课程需要点击一下加号
                click element  css=[ng-click="addOne()"]                            #点击确定按钮
                sleep  1                    #让页面老师添加完成后,页面足够时间去渲染。否则代码操作过块,添加老师完成后面检查老师
                                            #也找不到这个现在添加的老师,强等一下

            检查老师
                [Arguments]   @{expect}              #@这样可以传很多参数,构造成一个列表
                click element   css=[ui-sref="teacher"]                             #进入老师菜单,go to也可以,但是go to可能会刷新页面
                ${teachers}  get webelements  css=tr td:nth-child(3)                 #获取登录名称元素
                ${list}  evaluate  [i.text for i in $teachers]                      #获取teacher的登录名的文本
                should be true    $expect == $list                              #校验判断来说是否在结果里面

    tc                            #testcase--测试用例 
        webui操作                #webui测试用例 
            __init__.robot        #webui层级的init初始化文件
                #存放套件目录初始化清除操作   打开浏览器和关闭浏览器
                *** Settings ***
                Resource  rflib/rc.robot
                Suite Setup  打开浏览器
                Suite Teardown  关闭浏览器

            管理员操作                    #测试套件 
                __init__.robot            #init配置文件,执行测试套件前会先执行init文件的内容
                    #存放套件目录初始化清除操作   打开浏览器和关闭浏览器
                    *** Settings ***
                    Resource  rflib/rc.robot
                    Suite Setup  登录动作
                    
                老师管理                #测试套件目录级别存放老师类型用例
                    新增老师.robot        #测试套件文件级别
                        *** Settings ***
                        Resource  rflib/rc.robot                    #相对路径导入法使用/,

                        *** Test Cases ***
                        添加老师1
                            [setup]  run keywords  删除所有老师   AND   删除所有课程
                            ...   AND  添加课程   初中语文  语文课   1
                            ...   AND  添加课程   初中数学  数学课   2
                            添加老师  许文强  许文强  语文老师  2  初中语文
                            检查老师  许文强
                            添加老师  丁力  丁力  数学老师  1  初中数学
                            检查老师  丁力  许文强

                        添加老师2
                            log to console  ${g_var}            #rf资源文件里面用关键字set global variable  ${g_var}  hello创建一个全局变量被这个套件使用
                            
                课程管理                #测试套件目录级别存放课程类型用例
                    新增课程.robot        #测试套件文件级别
                        *** Settings ***
                        Resource   rflib/rc.robot


                        *** Test Cases ***
                        添加课程1
                            [Setup]   删除所有课程
                            [Teardown]   删除所有课程
                            添加课程  appium课程  appium课程  1
                            检查课程  appium课程


                        添加课程2
                            [Setup]   删除所有课程
                            [Teardown]   删除所有课程
                            添加课程  selenium课程  selenium课程  2
                            检查课程  selenium课程

43:rf测试用例的执行:rf的命令格式

    robot [options]    data _sources        #robot执行命令的格式
        options 是rf命令选项,可以为空
        data _sources(数据数据源) 是执行测试套件文件或者目录的路径(绝对路径和相对路径都可以)相对当前shell的工作目录
            测试数据源可以指定多个
        robot t1.robot t2.robot t3.robot        #同时执行多个robot文件,可以是多个robot文件,也而可以是多个目录,也可也是文件+目录
        
        1:定制化报告名称:--name            
            --name 回归测试         支持中文英文
        
        2:通配符寻找多个测试套件   *
            robot --name t*.robot        #寻找以t开头robot结尾的测试套件
        
        3:用例里面执行用例筛新--指定某个用例        -t或者--test
            robot -t case1 t1.robot t2.robot t3.robot     #-t指定case1这个测试用例执行或者  --test case1
            -t case*            #-t也可也使用通配符
        
        4:rf还支持筛选套件   -s    
            robot --name 回归测试  -s s1 t*.robot suite                #只执行s1套件文件下面的所有用例
        
        5:初始化和清除 
            robot suite\s1\t1.robot                            #执行t1.robot 测试套件文件
                但是s1这个目录下面有一个目录级别的初始化和清除,robot suite\s1\t1.robot这样指定执行不会执行s1目录里面的初始化和清除
                因为指定的数据源是一个具体的robot文件,不认为你这个文件前面的也是套件目录,越过这些套件目录,不会执行套件目录里面的配置文件
                
                如果想要执行测试套件,又想要执行上层的初始化,数据源需要指定suite文件夹  然后-t筛选用例  -s刷选套件
            
            robot -s t1  suite
                    t1这里就是t1.robot,去掉文件后缀名,表示robot套件名称,-s指定套件,如果是目录就是目录名称,
                    如果是文件去掉文件后缀名称就是套件名称
                
                    分层级别的初始化和清除这样做很有必要
        
        6:参数文件用法:如果用例执行命令很长,可以将后面的命令添加到文件里面
            args.txt   文件 
              将robot执行命令放在文件中:
              
            args.txt                              #args文件存放以下内容,如果参数存放配置文件里面,每段参数需要用一行,不然执行会报错
              --name 回归测试  
              -s s1 
              t*.robot 
              suite    
              
              
              执行的时候指定参数文件  -A 
              robot -A E:\Users\ywt\PycharmProjects\ywtprj\args.txt                #绝对路径写法 
              robot -A args.txt                                                    #相对路径写法
        
            参数文件可以使用python文件生成,robot要执行的用例写在文本文件,执行的时候  robot -A args.txt        指定参数文件
        
        7:rf中的标签:测试用例可以有多个标签,根据任何一个标签可以过滤到该用例--
            1:给用例打标签
            2:筛选标签
            
            rf中两种标签
                1:强制标签   套件settings表里的Force Tags(强制标签)    测试套件里所有的测试用例都具有该tag
                                测试用例表的[tags]配置
                2:默认标签      settings表里的Default Tags     该测试套件里面没有[tags]设置的测试用例都具有该tag
                              测试目录里面的init.robot不执行Default Tags
                
                标签都是打在用例上面的,即使是定义在套件的settngs表里面也是作用在每个用例上面,减少每个用例重复打标签而已
            
            C:\Users\ywt\Desktop\lesson6>robot --include smokingtest t*.robot            #--include smokingtest  具备smokingtest标签的用例都执行
                rf标签筛选比pytest简单
            
            C:\Users\ywt\Desktop\lesson6>robot --include tag* t*.robot                    #--include tag*        执行所有tag开头的标签
            
            
        根据标签选择测试用例
            #执行包含标签'foo'的用例                                        --include foo
            #执行不包含标签'foo'的用例.                                        --exclude foo
            #执行同时包含标签'one', 'web test' 的用例.                        -- include oneAND"web test”
            #执行包含标签'one'或者'two'的用例.                                -- include oneORtwo
            执行包含 标签'one '但是不包含标签'two' 的用例。                    -- include oneNOTtwo
            #执行标签格式为W*W的用例,                                        --include w*w            
                
        
        C:\Users\ywt\Desktop\lesson6>robot --include tag1ANDsmoking* testsuit                筛选同时tag1标签和smoking标签开头的测试用例
        
        C:\Users\ywt\Desktop\lesson6>robot --include tag1NOTsmokingtest testsuit                #刷选具有标签tag1但是不具有标签smokingtest的用例
        
        C:\Users\ywt\Desktop\lesson6>robot --include tag1ORsmokingtest testsuit                    #筛选具备tag1或者smokingtest的用例,多选使用OR
        
        标签形式执行通配符  *
        
        标签可以空一个空格,但是执行的时候需要加引号如下:
            Force Tags  smoking test
            C:\Users\ywt\Desktop\lesson6>robot --include "smoking test" testsuit
        
        
    默认情况下rf里面的测试用例都是关键测试用例,rf持续集成,执行完毕,
        假如设置一条测试用例是关键测试用例,其他没有设置的默认都是非关键测试用例了
        
        通过 --critical(-c)和--nincritical(-n)            后面+tag名称(标签名称,不是用例名称)来指定测试用例是否是关键测试用例
        
            C:\Users\ywt\Desktop\lesson6>robot  -c tag1 testsuit              tag1指定为关键测试用例。其他的没有指定就不是关键测试用例了
                                                                            非关键测试用例假如执行不通过不影响测试结果,总的结果还是通过的
            
            C:\Users\ywt\Desktop\lesson6>robot  -n unstable  testsuit            指定打了标签unstable的是非关键测试用例,非关键执行失败没事,关键的通过就行
            
            关键个非关键可以控制rf的执行结果--结果成功所有关键测试用例执行通过才是执行成功,背景颜色绿色的、
            
        rf标签实例:
        lesson6
            testsuit
                __init__.robot
                    *** Settings ***
                    Force Tags  abctest                 #testsuit套件目录所有的用例都会打上abctest 的标签
                    #Default Tags  notag2                #这样写报错,测试目录里面的init.robot不执行Default Tags,Default Tags只能定义在文件里面    
                
                t1.robot
                    *** Settings ***
                    Force Tags  smokingtest  base
                    Default Tags  notag                     #表示当前用例自身没有定义标签,测试用例没有自己定义标签就打上这个默认标签

                    *** Test Cases ***
                    case1
                        [Tags]  tag1  tag2   tag3                       #一个用例可以具备很多标签
                        log to console  case1

                    login
                        [Tags]  login  tag1
                        log to console  login

                    logout
                        [Tags]  tag3
                        log to console  logout

                    shoplist
                        log to console  shoplist

                t2.robot
                    *** Settings ***
                    Force Tags  base

                    *** Test Cases ***
                    case1
                        log to console  case1

                    login
                        log to console  login

                    logout
                        log to console  logout        
                
                t3.robot
                    *** Test Cases ***
                    case3
                        [Tags]  unstable                        #打个标签不稳定的测试用例,
                        log to console  case3
                        fail                #制造失败的用例,执行测试报告会显示一个fail        

44:后续rf的使用:

  1:用户关键字  ---robot文件定义的

  2:自定义关键字--python开发,效率高

    python开发关键字保证写的python类被rf导入只生成一个浏览器对象:如下,假如有如下类,被多个rf套件导入就会生成多个浏览器---必须使用单例模式

    class weboptions:

      def  setupwebTest(self):

      driver=webdriver.Chrome()

 

    保证webdriver被rf导入只生成一个浏览器对象,单例模式,rf实现单例模式一个语句就可以实现了

      ROBOT_LIBRCRY_SCOPE= 'GLOBAL'  #确保导入库的时候初始化一个对象,从而保证全局使用一个浏览器

      rf在导入的时候只会生成一个对象,单例模式的具体操作rf来操作,不需要自己写代码来实现单例模式----rf简单

 

posted @ 2021-03-10 12:05  至高无上10086  阅读(1289)  评论(0编辑  收藏  举报