• pytest


    先看一段代码:

    class Test_错误密码:
    
        def test_C001001(self):
            print('\n用例C001001')
            assert 1 == 1
            
        def test_C001002(self):
            print('\n用例C001002')
            assert 2 == 2
            
        def test_C001003(self):
            print('\n用例C001003')
            assert 3 == 2
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    名称规范:

    1.必须以 test_ 开头,或者以 _test 结尾
    2.类名必须大写开头

    py中如何检查用例是否通过:

    assert 后面的表达式结果 为 True ,就是 检查点 通过,结果为False ,就是检查点 不通过。

    执行结果:

    在这里插入图片描述

    通过的用例 是用一个绿色小点表示, 不通过的用例用一个红色的F表示

    在这里插入图片描述
    打印信息

    用例代码中有些打印语句没有显示出内容。因为pytest 会 截获print打印的内容。如果我们希望 显示测试代码中print的内容,因为这些打印语句在调试代码时很有用,可以加上命令行参数 -s

    pytest -s
    
    • 1

    如果我们希望得到更详细的执行信息,包括每个测试类、测试函数的名字,可以加上参数 -v,这个参数可以和 -s 合并为 -sv

    pytest -sv
    
    • 1

    指定执行目录

    我们目前 项目根目录 中 只有一个cases 目录用例存放测试用例, 将来还会有其他目录,比如:

    lib目录存放库代码、cfg目录存放配置数据 等等。

    为了防止 pytest 到其他目录中找测试用例项,执行测试时,我们可以在命令行加上目标目录 cases ,就是这样

    pytest cases
    
    • 1

    清除

    模块化级别

    模块级别 的初始化、清除 分别 在整个模块的测试用例 执行前后执行,并且 只会执行1次 。

    def setup_module():
        print('\n *** 初始化-模块 ***')
    
    
    def teardown_module():
        print('\n ***   清除-模块 ***')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    类级别

    类级别 的初始化、清除 分别 在整个类的测试用例 执行前后执行,并且 只会执行1次

        @classmethod
        def setup_class(cls):
            print('\n === 初始化-类 ===')
    
        @classmethod
        def teardown_class(cls):
            print('\n === 清除 - 类 ===')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    它主要是用来为该 类 中的所有测试用例做 公共的 初始化 和 清除

    方法级别

    如下定义 setup_method 和 teardown_method 实例方法

        def setup_method(self):
            print('\n --- 初始化-方法  ---')
    
        def teardown_method(self):
            print('\n --- 清除  -方法 ---')
    
    • 1
    • 2
    • 3
    • 4
    • 5

    目录级别

    import pytest 
    
    @pytest.fixture(scope='package',autouse=True)
    def st_emptyEnv():
        print(f'\n#### 初始化-目录甲')
        yield
        
        print(f'\n#### 清除-目录甲')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    注意:这里清除环境的代码就是 yield 之后的代码。 这是一个生成器,具体的说明参见视频讲解。

    但是我发现了pytest一个重要的bug: 清除操作并不一定会在该目录最后一个测试用例执行完进行调用。在我看来,这个问题是非常大的。

    因为,一个目录下的用例执行完后,该清除的数据没有清除,这可能会导致其他目录下的用例执行失败。


    挑选用例执行

    指定一个模块

    可以像这样只挑选一个模块执行

    pytest cases\登录\test_错误登录.py
    
    • 1

    指定目录

    可以像这样只挑选一个目录执行

    pytest cases
    
    • 1

    也可以指定多个目录

    pytest cases1  cases2\登录
    
    • 1

    指定模块里面的函数或者类

    指定一个类

    pytest cases\登录\test_错误登录.py::Test_错误密码
    
    • 1

    也可以指定类里面的方法

    pytest cases\登录\test_错误登录.py::Test_错误密码::test_C001001
    
    • 1

    根据名字

    可以使用 命令行参数 -k 后面加名字来挑选要执行的测试项

    比如像这样后面跟测试函数名字的一部分:

    pytest -k C001001 -s
    
    • 1

    名字的规则:
    1.可以是测试函数的名字,可以是类的名字,可以是模块文件名,可以是目录的名字

    2.是大小写敏感的

    3.不一定要完整,只要能有部分匹配上就行

    4.可以用 not 表示选择名字中不包含,比如

    pytest -k "not C001001" -s
    
    • 1

    5.可以用 and 表示选择名字同时包含多个关键字,比如

    pytest -k "错 and 密码2" -s
    
    • 1

    6.可以用 or 表示选择名字 包含指定关键字之一即可,比如

    pytest -k "错 or 密码2" -s
    
    • 1

    根据标签打标签

    可以这样给 某个方法加上标签 webtest

    import pytest
    
    class Test_错误密码2:
    
        @pytest.mark.webtest
        def test_C001021(self):
            print('\n用例C001021')
            assert 1 == 1
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    指定标签运行:

    pytest cases -m webtest -s
    
    • 1

    也可以这样给整个类加上标签

    @pytest.mark.webtest
    class Test_错误密码2:
    
        def test_C001021(self):
            print('\n用例C001021')
            assert 1 == 1
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    注意:
    1.一个东西可以打多个标签
    2.标签支持中文

    @pytest.mark.网页测试
    @pytest.mark.登录测试
    class Test_错误密码2:
    
        def test_C001021(self):
            print('\n用例C001021')
            assert 1 == 1
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    可以这样定义一个全局变量 pytestmark 为 整个模块文件 设定标签

    import pytest
    pytestmark = pytest.mark.网页测试
    
    • 1
    • 2

    如果你需要定义多个标签,可以定义一个列表

    import pytest
    pytestmark = [pytest.mark.网页测试, pytest.mark.登录测试]
    
    • 1
    • 2

    数据驱动

    用例 UI-0001 到 UI-0005 这5个登录的测试用例,共同的特点是,它们测试步骤是一模一样的,只是输入的数据(用户名、密码)不同,要检查的输出数据(错误提示)不同。

    如果有一批测试用例,具有 相同的测试步骤 ,只是 测试参数数据不同 。自动化测试时,把测试数据从用例代码中 分离 开来,以后增加新的测试用例,只需要修改数据。这就是数据驱动。

    class Test_错误登录:
        @pytest.mark.parametrize('username, password, expectedalert', [
            (None, '88888888', '请输入用户名'),
            ('byhy', None, '请输入密码'),
            ('byh', '88888888', '登录失败 : 用户名或者密码错误'),
            ('byhy', '8888888', '登录失败 : 用户名或者密码错误'),
            ('byhy', '888888888', '登录失败 : 用户名或者密码错误'),
        ]
                                 )
        def test_UI_0001_0005(self, username, password, expectedalert):
            alertText = loginAndCheck(username, password)
            assert alertText == expectedalert
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    fixture

    前面讲初始化清除时,主要用的是 unittest 风格的初始化清除方法。pytest里面有更灵活方便的初始化、清除 方法,就是使用 fixture。

    import pytest
    
    # 定义一个fixture函数
    @pytest.fixture
    def createzhangSan():
        # 这里代码实现了使用API创建用户张三账号功能
        print('\n *** createzhangSan ***')
        zhangSan = {
            'username'   : 'zhangsan',
            'password'   : '111111',
            'invitecode' : 'abcdefg' # 这是系统创建用户产生的其它信息
        }
        return zhangSan
    
    
    # 这里测试张三账号登录的功能
    def test_A001001(createzhangSan):
        print('\n用例 A001001')
        print('\ninvitecode is', createzhangSan['invitecode'])
    
    
    # 这里测试其它功能,不需要张三账号
    def test_C001001():
        print('\n用例 C001001')
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    结果:

    collected 2 items
    
    test_1.py::test_A001001
     *** createzhangSan ***
    
    用例 A001001
    
    invitecode is abcdefg
    PASSED
    
    test_1.py::test_C001001
    用例 C001001
    PASSED
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    参数名和fixutre 函数同名,就会自动执行它, 这和我们以往理解的python不一样啊。是的,这就是 pytest 的 denpendcy injection 中文称之为 : 依赖注入 。不要被这些名词吓懵了,这就是pytest的做法而已。

    我们的测试函数 都是 pytest 搜集和调用的,pytest会做这样的分析:如果发现 测试函数参数 和 某个 fixture 同名,就知道:要在执行该测试时先调用该 fixture函数, 然后把fixture函数返回的结果 作为 调用测试函数该参数的值。

    仅此而已,所谓依赖注入,没有什么神秘的。

    清除

    如果需要清除,最推荐的使用 yield 代替 return , yield后面的代码就是清除部分的代码。

    import pytest
    
    # 定义一个fixture函数
    @pytest.fixture
    def createzhangSan():
        print('\n ***  执行创建张三账号的代码 ***')
        zhangSan = {
            'username'   : 'zhangsan',
            'password'   : '111111',
            'invitecode' : 'abcdefg' # 这是系统创建用户产生的其它信息
        }
    
        yield zhangSan
    
        print('\n ***  执行清除张三账号的代码 ***')
    
    def test_A001001(createzhangSan):
        print('\n用例 A001001')
        print('\ninvitecode is', createzhangSan['invitecode'])
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    结果:

    test_1.py::test_A001001
     ***  执行创建张三账号的代码 ***
    
    用例 A001001
    
    invitecode is abcdefg
    PASSED
     ***  执行清除张三账号的代码 ***
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    fixture 参数

    如果多个测试函数的代码都一样,就是前面说的数据驱动,用例函数可以合并起来,使用多个参数,像这样

    @pytest.mark.parametrize("createUser",
        [("zhangsan", "111111"),("lisi", "111")],
        indirect=True)
    def test_A00100X(createUser):
        print('\nusername is', createUser['username'])
        print('测试行为')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    scope 范围

    函数级别

    函数级别 的fixture ,初始化、清除 会再每个函数或者方法执行前面各调用一次。fixture 的 scope 缺省值 ,就是 function , 所以也可以不指定

    类级别
    类级别 的初始化、清除 分别 在整个类的测试用例 执行前后执行,并且 只会执行1次

    模块级别
    模块级别 的初始化、清除 分别 在模块的 所有测试方法 执行前后执行,并且 只执行1次

    目录级别
    目录级别的 初始化清除,就是针对整个目录执行的初始化、清除。目录级别的初始化、清除 在 整个目录 所有用例 执行前后 分别 只执行一次

    整个测试级别
    整个测试级别的 初始化清除,就是针对 整个测试 执行的初始化、清除。

    整个测试级别的初始化、清除 在 整个测试 所有用例 执行前后 分别 只执行一次


    自动使用 fixture

    前面的fixture示例,都需要 用例函数 在参数里面 申明使用,才会调用的。如果没有用例 声明使用,就不会调用这个fixture。

    如果,我们想不声明,就默认使用,可以指定fixture的参数 autouse 的值为 true

    import pytest
    
    @pytest.fixture(scope='module', autouse=True)
    def createzhangSan():
        print('\n ***  创建数据环境 ***')
    
        yield
    
        print('\n ***  清除数据环境 ***')
    
    
    def test_C001():
        print('\n用例C001')
    
    
    def test_C002():
        print('\n用例C002')
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    这种自动使用fixture的情况,通常是 fixture 没有返回值 需要给用例使用的情况。

  • 相关阅读:
    MPI学习笔记(二):矩阵相乘的两种实现方法
    进程间通信IPC-共享内存(System V)
    关于 Database Administrators
    数据结构笔记——树与二叉树
    别再说你不知道分布式事务了
    活动报名|9月24日 Apache Flink Meetup · 北京站,Flink 1.16 新版本发布!
    华为云Astro的前世今生:用7年时间革新低代码开发观念
    LeetCode只出现一次的数字
    uniapp踩坑
    记录--使用Vue开发Chrome插件
  • 原文地址:https://blog.csdn.net/qq_52563729/article/details/133167372