跳转至

hytest 框架

声明

hytest 自动化软件测试框架 永久 开源免费,请大家放心使用。

项目 Github 网址


可以访问 GitHub 的朋友,学完hytest框架如果觉得不错,请帮我在 GitHub 上 点赞加一颗星

hytest 简介

点击这里,边看视频讲解,边学习以下内容

hytest (黑羽test)是白月黑羽自己研发的自动化测试框架,它非常适合 做 系统测试 自动化, 而相比之下,pytest、unittest 更适合白盒的单元测试、集成测试。

它有如下优点:

  • 上手非常简单

hytest 让大家直接用 Python 来写测试用例。

如果你有 Python编程经验,1小时就可以上手,1天就可以灵活使用。

  • 操作直观易懂

测试用例以 目录文件结构存放,清晰明了

初始化清除 机制清晰灵活

可以灵活地 挑选 要执行的测试用例

  • 漂亮的测试报告


本教程 通过一个网站系统的自动化 为例 来讲解 hytest。

如果你 还不了解 web 自动化,请先 学完我们的 selenium 教程。

点击这里先学习Selenium Web自动化

Note

以前我们开发过基于 robotframework 的自动化框架 - 黑羽robot

但是现在我们推荐使用 黑羽test 。

如果您依然想使用黑羽robot, 可以点击这里查看相关教程

安装、执行

安装 hytest 非常简单,执行如下命令即可:

pip install hytest 

注意

hytest 需要 Python 3.8 或者 更高版本


执行hytest 自动化非常简单:

只需要新建一个 项目目录 , 里面创建一个名为 cases 的子目录用来存放测试用例代码即可。

我们可以使用示例代码先看看。

点击这里下载zip文件, 解压后产生一个 cases示例目录。


这里目录里面的结构和自动化用例代码,后面会有讲解。


我们先运行hytest自动化测试,看看效果如何。

非常简单,只需要

  • 打开命令行窗口

  • 进入自动化代码根目录,也就是 cases 的上层目录 (注意不是cases目录,是cases的父目录)

  • 运行 hytest

运行命令 hytest 实际上就是执行如下的命令

python -m hytest.run


如果你是苹果Mac系统, 安装时或许不能自动产生hytest可执行程序,这时可以执行命令

python3 -m hytest.run


大家可以看一下执行后的命令行窗口界面输出,展示了执行每个用例的结果。

执行自动化,会产生一个log目录。里面有详细的 测试日志 和 测试报告。

测试报告是HTML格式的,会自动在浏览器中打开,可以通过右上角的浮动菜单:

  • 切换 精简、详细 两种模式 方便浏览内容

  • 跳转到下一个、上一个错误 方便在出现问题是,快速定位到问题所在点


如果你是给国外做自动化测试,需要产生英文版的报告,只需要加上 参数 --lang=en

如下

hytest  --lang=en

或者

python -m hytest.run  --lang=en

注意英文版的支持,必须使用 hytest 0.8.1 或者以后的版本


如果你不希望执行完测试后,自动打开测试报告,可以使用参数 --auto_open_report=no

如下

hytest --auto_open_report=no

该功能参数,也是 hytest 0.8.1 或者以后的版本 才支持。


关于hytest 命令行的参数使用,后面会有详细讲解,以后如果忘了某个命令参数可以执行 hytest -h 来查看参数说明。


以后真正做项目自动化的时候,通常还需要创建一些其他的目录,比如

  • lib 目录

用来存放 测试用例 需要使用的共享代码库。

  • doc 目录

存放 一些说明文档


可以使用命令参数 --new 来创建一个 hytest 的自动化项目目录,里面会包含一个cases目录和一个示例代码文件。

比如,执行

hytest --new auto1

就会在当前目录下创建名为 auto1 的 项目目录。

用例目录结构

点击这里,边看视频讲解,边学习以下内容

我们先了解一下用例目录的 结构

  • 自动化测试用例 是 写在 python 文件 中的 一个 python 类

对应一个测试用例文档里面的用例

  • 一个 代码文件 可以存放 多个用例

  • 多个代码文件可以用目录组织起来


cases 目录下面的 每个目录 和 py 文件 都 被称之为 测试套件(suite)

测试套件测试用例的集合 , 通俗的说,就是 一组用例

为例方便管理,我们把功能相关的测试用例组合起来放在一起,成为某个测试套件。

  • 包含 测试用例类 的 python 文件 称之为一个 套件文件

  • 包含 套件文件的 目录 称之为 测试套件目录

用例类的定义

用例文件格式如下:

文件里面每个类对应一个用例。

  • 类的 name 属性 指定 用例名

如果没有name 属性,那么类名就会被当做是用例名称

  • 类的 teststeps 方法 对应 测试步骤 代码

测试步骤代码 就是自动化地 一步步执行测试用例 的程序。

所以一个类 必须要有 teststeps 方法,才会被 hytest 当做是一个测试用例类。

比如

# 建议:类名 对应 用例编号
class UI_0101:
    # 测试用例名字,也建议以用例编号结尾,方便 和 用例文档对应
    # 也方便后面 根据用例名称 挑选执行
    name = '管理员首页 - UI-0101'

    # 测试用例步骤
    def teststeps(self):


为了使我们的测试日志和报告 更加清晰的展示执行的过程, 我们可以调用 hytest 的一些库函数,输出 执行步骤、提示信息 、检查点信息。


代码最前面加上 如下代码 ,导入 hytest 库里面 的常用 函数

from hytest import STEP, INFO, CHECK_POINT

class UI_0101:

    def teststeps(self):

        STEP(1,'打开浏览器')
        var1 = 'sdf'
        INFO(f'var1 is {var1}')
        CHECK_POINT('打开成功', var1.startswith('1sd') ) 


        STEP(2,'登录')        
        CHECK_POINT('检查登录是否成功', True) 

        STEP(3,'查看菜单') 
        CHECK_POINT('检查菜单是否正确', True)  
  • STEP 函数

用来声明每个测试步骤,这样日志报告更清晰

  • INFO 函数

用来打印一些信息在日志和报告中,方便出现问题时定位。

当然,如果你在开发调试阶段也可以直接使用print,在终端查看内容

  • CHECK_POINT 函数

用来声明测试过程中的每个检查点,任何一个检查点不通过,整个测试用例就被认为不通过。

缺省情况下,一个检查点不通过,后面的测试代码就不会继续执行。

如果你希望 某个检查点即使不通过,后续代码仍然继续执行,可以使用参数 failStop=False ,如下所示

 def teststeps(self):

     CHECK_POINT('即使不通过也不中止1', False, failStop=False)

     CHECK_POINT('即使不通过也不中止2', False, failStop=False)


具体的使用示例,参考讲解视频。


一个例子

点击这里,边看视频讲解,边学习以下内容

要讲解 自动化测试,需要一个 被测系统

我们使用 白月SMS系统 作为被测系统。

请大家点击这里,按照说明下载、安装、运行 白月SMS系统

针对该系统,现在有一批测试用例,准备自动化。 点击这里下载 白月SMS系统的测试用例文档

我们先完成用例 UI-0101 的自动化


代码开发,请看视频讲解。对应的参考代码如下

参考代码如下

from hytest import *
from selenium import webdriver

class UI_0101:

    name =  '检查操作菜单 UI_0101'

    def teststeps(self):

        STEP(1,'登录网站')

        options = webdriver.ChromeOptions()
        options.add_experimental_option('excludeSwitches', ['enable-logging'])

        wd = webdriver.Chrome(options=options)
        wd.implicitly_wait(10)

        wd.get('http://127.0.0.1/mgr/sign.html')

        wd.find_element_by_id('username').send_keys('byhy')
        wd.find_element_by_id('password').send_keys('88888888')

        wd.find_element_by_tag_name('button').click()

        STEP(2,'获取左侧菜单信息')

        eles = wd.find_elements_by_css_selector('.sidebar-menu li span')

        menuText = [ele.text  for ele in eles]

        INFO(menuText)

        STEP(3,'检查菜单栏')

        CHECK_POINT('左侧菜单检查', menuText[:3] == ['客户','药品', '订单'])

        wd.quit()

公共代码放入库中

点击这里,边看视频讲解,边学习以下内容

我们再来实现测试用例 UI-0102

和前面的操作一样,我们写一个新的类 UI_0102 对应 这个用例。

然后拷贝以前 Selenium 课程中已经实现的代码 放入到 teststeps 方法中。

参考代码如下

class UI_0102:
    name = '添加客户 UI_0102'

    def teststeps(self):
        STEP(1, '登录网站')

        options = webdriver.ChromeOptions()
        options.add_experimental_option('excludeSwitches', ['enable-logging'])

        wd = webdriver.Chrome(options=options)
        wd.implicitly_wait(10)

        wd.get('http://127.0.0.1/mgr/sign.html')

        wd.find_element_by_id('username').send_keys('byhy')
        wd.find_element_by_id('password').send_keys('88888888')

        wd.find_element_by_tag_name('button').click()


        STEP(2, '点击左侧客户菜单')

        # 先找到上层节点,缩小查找范围
        sidebarMenu = wd.find_element_by_class_name('sidebar-menu')

        # 再找到内部元素
        elements = sidebarMenu.find_elements_by_tag_name('span')

        # 第一个span对应的菜单是 客户,点击它
        elements[0].click()


        STEP(3, '添加客户')

        # 点击添加客户按钮
        wd.find_element_by_class_name('glyphicon-plus').click()

        # form-contorl 对应3个输入框
        inputs = wd.find_elements_by_css_selector('.add-one-area .form-control')

        # 输入客户姓名
        inputs[0].send_keys('南京中医院')
        # 输入联系电话
        inputs[1].send_keys('2551867858')
        # 输入客户描述
        inputs[2].send_keys('江苏省-南京市-秦淮区-汉中路-16栋504')

        # 第1个 btn-xs 就是创建按钮, 点击创建按钮
        wd.find_element_by_css_selector('.add-one-area .btn-xs').click()

        # 等待1秒
        sleep(1)


        STEP(4, '检查添加信息')

        # 找到 列表最上面的一栏
        item = wd.find_elements_by_class_name('search-result-item')[0]

        fields = item.find_elements_by_tag_name('span')[:6]

        texts = [field.text for field in fields]
        INFO(texts)

        # 预期内容为
        expected = [
            '客户名:',
            '南京中医院',
            '联系电话:',
            '2551867858',
            '地址:',
            '江苏省-南京市-秦淮区-汉中路-16栋504'
        ]

        CHECK_POINT('客户信息和添加内容一致 ',
                    texts == expected)

        wd.quit()


仔细检查代码我们可以发现 用例1 和 用例2 的登录代码是 完全相同的。

为了 去除 冗余代码,我们应该 把登录的代码放到一个单独的库文件中,

比如,新建 lib 目录,并且在下面新建一个 库文件 webui.py 。 其代码如下:

from selenium import  webdriver
from hytest import *

def open_browser():
    INFO('打开浏览器')
    options = webdriver.ChromeOptions()
    options.add_experimental_option('excludeSwitches', ['enable-logging'])

    wd = webdriver.Chrome(options=options)
    wd.implicitly_wait(10)

    GSTORE['wd'] =  wd


def mgr_login():

    wd = GSTORE['wd']

    wd.get('http://127.0.0.1/mgr/sign.html')

    wd.find_element_by_id('username').send_keys('byhy')
    wd.find_element_by_id('password').send_keys('88888888')

    wd.find_element_by_tag_name('button').click()


注意: 我们在创建 WebDriver 对象后,把它存到了 hytest 全局存储对象 GSTORE 中。 方便其他的代码 获取。


然后修改测试用例,里面使用 库文件 webui.py

如下所示

from lib.webui import  *

class UI_0102:   

    # 测试用例步骤
    def teststeps(self):

        STEP(1, '登录网站')
        open_browser()
        mgr_login()

        # 下面是其余代码

初始化、清除

点击这里,边看视频讲解,边学习以下内容

仔细分析,上面两个用例的自动化仍然有问题。

两个用例都要 打开浏览器登陆

如果有100个这样的用例,就执行100次登录的操作。

这两个用例 重点其实不在登录,而是后面的操作。

两个用例后面的操作 需要的初始环境是一样的 : 打开浏览器并且登录 的环境

能不能共享执行环境?

让这两个用例开始执行的时候,就处于 打开浏览器并且登录 的环境

就是我们用例执行时,就获取一个 WebDriver对象, 对应 管理员账号已经登录状态 的浏览器,后面的代码直接就可以使用这个 WebDriver对象 执行操作。


怎么 让自动化 用例执行的时候,就有一个 打开浏览器并且登录 的 初始环境 呢?

这就需要 初始化 (英文叫 setup )操作

初始化 就是: 为 一个或者多个测试用例执行时,构建所需要的数据环境。

与初始化正好相反的操作就是 清除 (英文叫 teardown )。

初始化 是 创建环境,清除 是 还原(或者说销毁) 环境

为什么需要 清除 来 还原环境?

因为 执行完测试用例后 可能会对数据环境产生改变,这种改变后的数据环境,可能会影响 其它用例的执行(不需要这种数据环境的用例)

比如:

用例A 测试系统中存在用户账号,使用该账号进行登录,期望结果是登录成功

用例B 测试系统中没有任何账号,使用不存在的账号进行登录,期望结果是登录失败。

为了执行用例A,我们初始化操作里面创建了一个账号user1

执行完后,需要执行用例B,那么这个创建的user1账号,就破坏了用例B所需要的数据环境(系统中没有账号)

这就需要在执行完用例A 后,执行一个 清除(还原)操作, 把添加的用户账号user1 删除掉。


可能有的朋友说,那也可以在用例B的初始化操作里面删除所有账号啊。

那样做,会使得 每个用例的初始化 工作变得非常难。 因为 不知道自动化测试的时候,会执行哪些用例,这些用例执行后 可能会产生什么多余的数据。

所以一个原则是:

做的 初始化 操作对环境产生了 什么改变

就应该在 清除 操作里面做什么样的 还原

hytest 的初始化/清除 支持 3种方式

  • 单个用例的初始化、清除
  • 整个用例文件的初始化、清除
  • 整个用例目录的初始化、清除

单个用例

首先看第一种:

单个用例的初始化、清除 是在 用例对应的类里面添加setup、teardown 方法

class c0101:
    name = '管理员首页 - UI-0101'

    # 初始化方法
    def setup(self):
        open_browser()
        mgr_login()

    # 清除方法
    def teardown(self):
        wd = GSTORE['wd']
        wd.quit()

    # 测试用例步骤
    def teststeps(self):        


hytest 执行用例时

  • 先执行 setup 里面的代码

  • 再执行 teststeps 里面的代码

  • 最后再执行 teardown 里面的代码。

而且

如果 setup 执行失败(有异常), 就不会再执行 teststeps 和 teardown 里面的代码了。

如果 teststeps 执行失败, 仍然会执行 teardown , 确保环境被 清除

用例文件

点击这里,边看视频讲解,边学习以下内容

精明的读者肯定已经发现,上面这种单个用例的初始化、清除,并没有解决我们前面说的 多个用例 共享数据环境的问题。

这时,我们可以使用 整个用例文件的初始化、清除

就是在 文件中 添加全局函数 suite_setupsuite_teardown

如下所示

from hytest  import *
from lib.webui import  *
from time import sleep

def suite_setup():
    INFO('suite_setup')
    open_browser()
    mgr_login()

def suite_teardown():
    INFO('suite_teardown')
    wd = GSTORE['wd']
    wd.quit()

class c0101:
    # 测试用例名字
    name = '管理员首页 - UI-0101'

    def teststeps(self):
    # 此处省略 测试用例步骤代码    


class c0102:
    name = '管理员首页 - UI-0102'

    def teststeps(self):
    # 此处省略 测试用例步骤代码    


如果一个 用例文件 既有 suite_setup、suite_teardown ,用例里面也有 setup、teardown , 执行的顺序如下

image

套件目录

刚才我们做到了让一个用例文件里面,所有的用例都共享初始化、清除操作。

如果 多个用例文件里面,的用例都需要相同的初始化清除操作怎么办? 比如目录结构

这时,我们可以使用 整个用例文件的初始化、清除

除了登录测试,其他所有的web界面操作都需要 打开浏览器登录,否则也会导致多次打开浏览器。

可以把打开浏览器的操作设置为 web界面操作目录 共同的初始化

把其他放到 登录后操作 目录中, 添加登录后操作的 初始化

那么怎么设置一个目录共同的初始化呢?

就是 在这个目录下面创建名为 __st__.py 的文件。 注意:前后都是 两个 下滑线

和套件文件一样,套件目录的 的初始化、清除 也是在 文件中 添加全局函数 suite_setup、suite_teardown。

请大家根据我们的视频 修改用例目录结构,加上 合适的 初始化、清除 代码。

如果 套件目录有 suite_setup、suite_teardown, 用例文件也有 suite_setup、suite_teardown ,用例里面也有 setup、teardown , 执行的顺序如下所示

image


缺省初始化、清除

用例文件 除了 可以使用 suite_setup、suite_teardown 对整个套件进行初始化清除,还支持另外一种初始化清除: 缺省初始化、清除

就是定义 名称为 test_setuptest_teardown 的全局函数。

如果在 用例文件 中定义了 全局函数 test_setup , 该文件中某个用例 本身没有初始化 方法, 执行自动化的时候就会 使用这个 test_setup 来初始化

如果在 用例文件 中定义了 全局函数 test_teardown ,该文件中某个用例 本身没有清除 方法, 执行自动化的时候就会 使用这个 test_teardown 来清除



数据关联

在初始化操作里面,经常会有创建一些数据,这些数据要在后面的用例中使用。

这对于 用例类内部的初始化数据方法setup 来说,很简单, 因为测试步骤和清除方法的都是同一个类的。

hytest框架执行时,会为该类创建实例。 所以只需要放入self实例属性即可


对于 用例文件或者 整个套件目录的初始化函数, 怎么把 suite_setup 函数 产生的数据给里面的用例使用呢?

前面示例其实已经讲过,可以使用hytest内置的对象 GSTORE

这个 GSTORE

可以使用字典式的赋值和取出元素,比如

from hytest import GSTORE

def suite_setup():
    GSTORE['环境1产品id'] = createProduct()
    GSTORE['driver'] = webdriver.Chrome()

def suite_teardown():
    deleteProduct(GSTORE['环境1产品id'])
    GSTORE['driver'].quit()


class c00303:
    name = '添加订单 - API-0303'

    def teststeps(self):
        createOrder(productid=GSTORE['环境1产品id'])


在 版本0.8.5 以后,GSTORE 也可以进行属性赋值和取值,比如

def suite_setup():
    GSTORE.productId = createProduct()
    GSTORE.driver = webdriver.Chrome()

def suite_teardown():
    deleteProduct(GSTORE.productId)
    GSTORE.driver.quit()


class c00303:
    name = '添加订单 - API-0303'

    def teststeps(self):
        createOrder(productid=GSTORE.productId)

这样写起来更简单,但是属性名要符合python的变量名规则,就不能有空格、加减号 之类的字符了。


使用内置的GSOTRE有个缺点,就是不知道添加的数据类型,IDE没法辅助,比如不能自动补齐对象的方法。

其实可以自己定义全局存储对象,并且定义里面的数据和类型。

比如,可以在项目根目录下面创建一个share.py文件,内容如下

from selenium import webdriver
class gs:
    driver : webdriver.Chrome 
    productId : int

然后用例文件、 __st__.py 文件就可以这样使用了

from share import gs

def suite_setup():
    gs.productId = createProduct()
    gs.driver = webdriver.Chrome()

def suite_teardown():
    deleteProduct(gs.productId)
    gs.driver.quit()


class c00303:
    name = '添加订单 - API-0303'

    def teststeps(self):
        createOrder(productid=gs.productId)


gs 定义的时候,里面的各个属性就有类型声明,后面使用该属性时,IDE就可以进行代码辅助了。

数据驱动 - data driven

点击这里,边看视频讲解,边学习以下内容

做过自动化测试的朋友经常听说过 数据驱动 (或者面试的时候被问到过)。

什么是数据驱动?

如果有一批测试用例,具有 相同的测试步骤 ,只是 测试参数数据不同

自动化测试时,把测试数据从用例代码中 分离 开来,以后增加新的测试用例,只需要修改数据。

这就是数据驱动。


举个例子:

某系统 登录功能的测试,有一批测试用例,其执行的步骤几乎都是一样的,只是使用的测试参数不同。

比如:

  • 不输入用户名,输入正确密码

  • 输入比正确用户名后面少一个字符,输入正确密码

  • 输入比正确用户名后面多一个字符,输入正确密码

  • 输入比正确用户名前面少一个字符,输入正确密码

  • 输入比正确用户名前面多一个字符,输入正确密码

  • 输入正确用户名,不输入密码

  • 输入正确用户名,输入比正确密码后面多一个字符


这种情况可以使用 hytest用例 的 数据驱动格式,只需如下定义即可

class c00003x:
    # ddt_cases 里面每个字典元素 定义一个用例的数据
    # 其中: name是该用例的名称, para是用例的参数
    ddt_cases = [
        {
            'name' : '登录 - 000031',
            'para' : ['user001','888888']
        },
        {
            'name' : '登录 - 000032',
            'para' : ['user0012','888888']
        },
        {
            'name' : '登录 - 000033',
            'para' : ['ser001','888888']
        }
    ]

    # 调用时,
    # hytest 框架执行时,会自动创建出3份用例实例
    # 并且在调用 teststeps时,把每个用例的参数设置在 self.para 中
    # 用例代码 可以直接从 self.para 中获取参数  
    def teststeps(self):
        # 取出参数
        username, password = self.para

        # 下面是登录测试代码

这样,我们就不需要定义那么多的测试用例类了, 而且测试数据也可以集中存放。


视频讲解中的 测试用例代码如下

from time import sleep
from lib.webui import *


class UI_000x:

    ddt_cases = [
        {
            'name': '登录 UI_0001',
            'para': [None, '88888888','请输入用户名']
        },
        {
            'name': '登录 UI_0002',
            'para': ['byhy', None, '请输入密码']
        },
        {
            'name': '登录 UI_0003',
            'para': ['byh', '88888888','登录失败 : 用户名或者密码错误']
        }
    ]


    def teststeps(self):
        wd = GSTORE['wd']

        wd.get('http://127.0.0.1/mgr/sign.html')

        username, password, info = self.para

        if username is not None:
            wd.find_element_by_id('username').send_keys(username)

        if password is not None:
            wd.find_element_by_id('password').send_keys(password)

        wd.find_element_by_tag_name('button').click()

        sleep(2)

        notify = wd.switch_to.alert.text

        CHECK_POINT('弹出提示', notify == info)

        wd.switch_to.alert.accept()


    def teardown(self):
        wd = GSTORE['wd']
        wd.find_element_by_id('username').clear()
        wd.find_element_by_id('password').clear()


动态产生驱动数据

点击这里边看视频,边看下面的教程

hytest 的驱动数据通常是固定的数据,当然也可以产生一些动态的数据

比如

from hytest import *

class UI_000x:

    ddt_cases = []
    for i in range(10):
        ddt_cases.append({
            'name': f'登录 UI_000{i+1}',
            'para': [None, f'{i+1}'*8,'请输入用户名']
        })

    def teststeps(self):
        INFO(f'{self.para}')


hytest 的运行分为两个阶段

  • 收集测试用例

这个阶段会搜集所有的用例目录下面的代码中的用例类,

把这些类实例化, 从而创建 用例实例对象

  • 执行测试用例

依次执行 上一步中创建的 用例实例对象


所以第一步中 搜集创建用例对象,是在 执行用例之前

所以ddt_cases 里面 不能使用用例运行时 才会产生的数据

比如

from hytest import *

# 套件初始化
def suite_setup():
    GSTORE['ddt_cases_UI_000x'] = []    
    for i in range(10):
        GSTORE['ddt_cases_UI_000x'].append({
            'name': f'登录 UI_000{i+1}',
            'para': [None, f'{i+1}'*8,'请输入用户名']
        }) 

class UI_000x:
    # 想使用套件初始化 设置的GSTORE数据??不行!!!! 
    # 因为 ddt_cases 收集阶段就会执行,这时还没有执行suite_setup()
    # 这时 GSTORE里面还是空的,所以会报错
    ddt_cases = GSTORE['ddt_cases_UI_000x']

    def teststeps(self):
        INFO(f'{self.para}')

挑选用例执行 - 名称方式

点击这里,边看视频讲解,边学习以下内容

执行自动化测试的时候,我们往往并不需要执行 全部的 测试用例。

比如:冒烟测试,只需要测试冒烟测试的那些用例。 或者调试自己写的某个用例的自动化,就只需要执行那一个用例。

hytest 可以灵活的挑选要执行的测试用例。

我们可以通过 --test 或者 --suite 命令行参数 来指定执行哪些用例或者套件,而且还支持用通配符的方式。

--test testA                # 执行名为 testA 的用例
--test testA --test testB  # 执行名为 testA 和  testB 的用例
--test test*              # 执行名字以 test 开头的用例
--suite 订单管理              # 执行 名为 订单管理 的套件

比如,我们想只测试 药品管理 这个套件

hytest --suite  药品管理  

比如,我们想只测试 界面 - UI-0101 这个用例

hytest --test  "界面 - UI-0101"  

因为用例名中有空格,所以必须用双引号包起来。

通常,我们的用例名后面会包含用例的 ID, 这样就可以很方便的根据用例ID,来选择用例了

比如 这样

hytest --test  *0101  

就可以挑选我们 练习中的 用例名为 界面 - UI-0101 的用例执行


假如,你的测试领导,要求做冒烟测试, 挑选出来的用例编号为以下这些:

UI-0301
UI-0302
UI-0303
UI-1401
UI-1402

我们就可以这样执行

hytest --test *0301  --test *0302 --test *0303 --test *1401 --test *1402

大家自然会想到,如果要执行的用例太多,比如 1000 个,命令行参数岂非太长了?

这时我们可以使用参数文件,可以把所有的参数都放在参数文件中,比如,创建一个名字为 args 的参数文件,内容如下

--test *0301
--test *0302
--test *0303
--test *1401
--test *1402

一行一个参数

然后, 我们的命令就只需要 hytest -A args 就可以了

挑选用例执行 - 标签方式

点击这里,边看视频讲解,边学习以下内容

hytest 还有一种选择测试用例的方法:根据用例的 标签 来挑选用例

给用例添加标签

我们可以给测试用例打上标签(tag),这样在运行的时候,可以通过标签指定要运行哪些用例。

标签 就是用例的属性特征描述

测试用例可以有多个标签描述它的属性特征, 比如一个登录测试的用例, 可以有3个标签: 登录功能、冒烟测试、UI测试

hytest 可以根据设置的标签 选择执行该用例。 这个后面会讲


给用例添加标签有如下几种方式

  • 全局变量 force_tags

如果我们在测试用例文件 定义了一个名为 force_tags 的全局变量,格式如下

force_tags = ['登录功能','冒烟测试','UI测试']

那么该文件里面所有测试用例都具有了这些标签。

标签一定要放在列表中,即使只有一个标签


如果我们在测试套件目录初始化文件__st__.py定义了一个这样的 force_tags 全局变量, 那么该目录里面所有测试用例都具有了该tag

  • 测试用例类的 tags 属性

如果我们在测试用例类 定义了一个名为 tags 属性,格式如下

class c00001:
    name = '添加订单 - 00001'
    # 用例标签,可选   
    tags = ['登录功能','冒烟测试','UI测试']

那么本测试用例就具有了这些标签。


根据标签挑选

在执行自动化的时候,我们可以通过命令行参数,指定标签,从而挑选要执行的测试用例

比如:

# 执行包含 标签 '冒烟测试' 的用例. 
--tag 冒烟测试  


# 执行不包含标签 '冒烟测试' 的用例.
--tagnot 冒烟测试 


# 执行 同时有 冒烟测试、UITest 两个标签的用例
--tag "'冒烟测试' and 'UITest'"


# 执行 有 冒烟测试 或者 UITest 标签 的用例
--tag 冒烟测试   --tag UITest


# 执行标签格式为 A*B 的用例,比如 A5B, AB, A444B
--tag A*B    

断点调试

点击这里,边看视频讲解,边学习以下内容

hytest 运行后,如果发现 程序出现了问题,怎么去 debug 呢?


最简单的方法,当然就是直接用print 打印出代码中关键的变量值。


更复杂的问题,需要一步步定位,使用断点调试更方便。

hytest 执行测试用例,其实就是 执行如下的命令

python -m hytest.run

上面意思就是执行 hytest 包里面的 run.py 文件

当然我们可以在pycharm中执行同样的命令进行断点调试

按照如下过程,配置运行参数

  • 点击配置选项

  • 然后,如下图所示,进行配置。注意下图方框里面要点击箭头,从 Script path 改为 Module name

image

这样设置,执行的时候,就等于执行了下面的命令

cd H:\my\autotest
python.exe -m hytest.run --test *0101
  • 然后再点击debug的图标,就可以进行断点调试了。

hytest vscode 扩展(插件)

我们开发了 hytest 的 vscode 插件, 方便大家使用vscode作为IDE时,开发、调试 hytest 自动化测试代码。


如果你对vscode不熟悉,可以先学习一下vscode的使用,点击这里观看视频讲解


使用 vscode 的 hytest 扩展,要确保 vscode 自动化项目 Python 解释器环境 选择正确。

就是:要选择 安装了 hytest 库 的那个解释器环境。


具体操作,点击观看这里的视频讲解

其他功能

指定测试报告标题

使用参数 --report_title 指定测试报告标题

比如

hytest --report_title 回归测试第2轮报告


如果标题中有需要有空格, 加上双引号,如下所示

hytest --report_title "回归测试第2轮 报告"


注意,该功能是 hytest v0.8.2 新加入的

报告中加入图片

点击这里,边看视频讲解,边学习以下内容

在用 selenium做 web自动化、Appium 做手机自动化 时, 可以使用hytest库中的函数 SELENIUM_LOG_SCREEN 进行截屏并写入测试报告中。

如下所示:

from hytest import *

class c1:
    name = 'web-lesson-0001'

    def teststeps(self):
        self.driver = webdriver.Chrome()
        self.driver.get('http://192.168.56.103/sign.html')

        # 第1个参数是 webdriver 对象
        # width 参数为可选参数, 指定图片显示宽度
        SELENIUM_LOG_SCREEN(driver, width='70%') 


如果你想 直接在报告中 插入 已经产生的图片,可以使用hytest库中的函数 LOG_IMG

如下所示:

from hytest import *

class c1:
    name = 'web-lesson-0001'

    def teststeps(self):

        # 第1个参数是 图片路径,可以是网络url 
        LOG_IMG('http://www.byhy.net/xxx.png')
        # 也可以是相对报告文件的本地路径
        LOG_IMG('imgs/abc.png')
        # width 参数为可选参数, 指定图片显示宽度
        LOG_IMG('imgs/abc.png', width='70%')

在Jenkins中触发运行

点击这里,边看视频讲解,边学习以下内容


当 执行 hytest 有参数 --report_url_prefix

比如: hytest --report_url_prefix http://192.168.5.156

执行完测试,就会把测试报告拷贝到 自动化项目的 reports 目录里面

并且测试结束会打印出 类似这样的一句话

测试报告 : http://192.168.5.156/report_20230108_180546.html


所以,在执行前,我们应该启动查看测试报告的web服务

比如 在自动化测试项目目录下面执行:

python -m http.server 80 --directory reports

就会把当前目录下的 reports 作为web访问的根目录

如果,执行测试的主机IP为 192.168.5.156

就可以通过 http://192.168.5.156 查看测试报告


假设要执行的 hytest 自动化项目目录路径为 d:/my/elife_autotest

Windows环境下,要在 Jenkins 控制器主机 直接执行 包含 hytest 自动化测试 的 Pipeline时

我们可以这样设置 Pipeline 的 Jenkinsfile

pipeline {
    agent any

    stages {
        stage('Build') {
            steps {
                echo 'build release'
            }
        }


        stage('Test') {
            steps {
                dir("d://my//elife_autotest") {
                    bat "hytest --report_url_prefix http://192.168.5.156"
                }
            }
        }
    }    

}


Jenkins 运行后,会在 build 结果中包含 hytest 的 控制台输出内容,也就包含了测试结果和报告链接,方便从Jenkins 视图查看。

和其他系统集成

点击这里,边看视频讲解,边学习以下内容

注意:下面的功能是 hytest 0.7.1 以后版本才支持。


在测试执行过程中, hytest 会发出 测试开始执行、每个用例执行结果、测试执行完毕 等事件。

开发者 可以 自己写代码, 处理这些事件,从而和测试使用的其他系统集成。

比如,你想 在测试的过程中,每执行完一个用例,就把测试结果写入到 测试记录系统中。

这个测试记录系统可能是一个Excel 文件,也可能是 Test Request、禅道、JIRA 之类的 测试管理系统。


这些代码可以定义在 hytest 用例根目录的 __st__.py 文件中。


下面的代码示例,展示如何 在测试过程中, 动态的把 测试结果写入 Excel测试用例执行 记录文件。

class MySignalHandler:
    TEST_RET_COL_NO = 6 # 测试结果在用例excel文件中的列数

    def __init__(self):
        self.caseNum2Row = {} # 用例编号->行数 表
        self.getCaseNum2RowInExcel()

        # 执行 pip install pypiwin32 确保安装了 win32com 所在的库
        import win32com.client
        self.excel = win32com.client.Dispatch("Excel.Application")
        self.excel.Visible = True
        workbook = self.excel.Workbooks.Open(r"h:\tcs-api.xlsx")
        self.sheet = workbook.Sheets(1)

    def getCaseNum2RowInExcel(self):
        """
        得到Excel 中用例 编号对应的行数,方便填写测试结果
        """
        import xlrd
        book = xlrd.open_workbook(r"h:\tcs-api.xlsx")
        sheet = book.sheet_by_index(0)
        caseNumbers = sheet.col_values(colx=1)
        print(caseNumbers)

        for row, cn in enumerate(caseNumbers):
            if '-' in cn:
                self.caseNum2Row[cn] = row + 1

        print(self.caseNum2Row)

    def case_result(self, case):
        """
        case_result 是 每个用例执行结束 ,会调用的函数

        @param case: 用例类 实例
        """

        # 找到对应的测试用例在excel中的行数
        caseNo = case.name.split(' - ')[-1].strip()
        cell = self.sheet.Cells(self.caseNum2Row[caseNo], self.TEST_RET_COL_NO)
        # 翻动滚动条,保证当前测试结果单元格可见
        self.excel.ActiveWindow.ScrollRow = self.caseNum2Row[caseNo]-2

        if case.execRet == 'pass':
            cell.Value = 'pass'
            cell.Font.Color =  0xBF00 # 设置为绿色 
        else:
            cell.Font.Color =  0xFF   # 设置为红色 
            if case.execRet == 'fail':
                cell.Value = 'fail'
            elif case.execRet == 'abort':
                cell.Value = 'abort'

    def test_end(self, runner):
        """
        test_end 是 整个测试执行完 ,会调用的函数

        @param runner :  hytest runner 对象
               runner.case_list:  列表,里面包含了所有用例类实例
        """
        for case in runner.case_list:
            print(f'{case.name} --- {case.execRet}')

# 注册这个类的实例 为一个 hytest 信号处理对象
from hytest import signal
signal.register(MySignalHandler())


整个示例下载地址如下,包括了 cases 目录 和 测试用例excel文件。

链接:https://pan.baidu.com/s/1ALhWwxbhCQQLRKJ1we4vBg 提取码:1111

您需要高效学习,找工作? 点击咨询 报名实战班

点击查看学员就业情况


课后练习

为了大家练习自动化测试,我们提供了一个 黑羽学院 互联网教学系统,点击这里查看如何使用、安装该系统

这里是说明视频 https://www.bilibili.com/video/BV1uy4y1E7Tp


注意:实战班学员只需要根据视频学习开发过程即可,不需要安装被测系统去做。后面有针对实战班学员的专门的项目实战


该系统的下载网盘中,有一个 黑羽学院测试用例 文档

上面的视频中讲解了,如何使用 hytest,完成文档中 测试用例的自动化

注意:

  • 做好 合适 的 初始化、清除

  • 用例公共代码要放入库中

  • 通过用例名 和 用例标签挑选用例执行

  • 出现问题,根据教程中讲解的方法进行 调试,解决问题