Python测试进阶(一)

news2024/11/15 11:51:42

文章目录

  • 测试框架
    • pytest
      • Mark
      • skip
      • 参数化
      • 异常处理
    • 数据驱动
  • Allure
    • 集成
    • 生成报告
  • Fixture
    • 基操
    • 作用域
    • yield
    • 数据共享
    • 自动应用
    • 参数化
  • ini
    • 运行规则
    • 配置命令行参数
    • 指定/忽略执行目录
    • 配置日志
  • 插件开发
    • 常用插件
    • 分布式并发
    • 自定义插件
    • 打包发布
  • hook
  • 小结

测试框架

  • 先了解unittest
  • 问题分析
    • 自动化测试前,需要准备好数据,测试完成后,需要自动清理脏数据
    • 自动化测试中,需要使用多套测试数据实现用例的参数化,有没有便捷的方式?
    • 自动化测试后,如何优雅的生成报告?
  • pytest框架可以满足以上的需求

pytest

  • 优点
    • 支持单元测试和复杂功能测试,兼容unittest
    • 结合requests实现接口测试,还可以结合selenium,appium实现自动化功能测试
    • 结合Allure集成到Jenkins实现持续集成
    • 支持300+插件并可以自定义插件
  • 安装
    • pip install -U pytest
  • 使用规则
    • 测试文件要以test_开头,或者_test结尾
    • 类要以Test开头
    • 方法/函数(无需继承类),要以test_开头
    • 测试类不能写__init__方法
  • PyCharm配置
    • 默认Test Runner
      1
    • demo
      def inc(x):
          return x+1
      
      def test_assert():
          assert inc(4) == 5
      
      class TestAnswer():
          def test_demo(self):
              assert 1==1
      
  • 命令行执行case方式
    • 运行:pytest,会执行当前目录下所有测试文件
    • 运行:pytest test_demo.py,执行这个文件的所有case
    • 运行:pytest test_demo.py::test_assert1,执行文件下某个函数case
    • 运行:pytest test_demo.py::TestDemo,执行这个文件的这个类下的所有case
    • 运行:pytest test_demo.py::TestAnswer::test_demo2,执行类下面某个方法case
    • 总之,通过::递进
  • 命令行参数
    • 参数介绍
      3
    • demo,具体的用例在下面
      4
      5
  • setup/teardown
    • 规则
      2
    • demo,在terminal使用-v -s参数运行
      # 模块(文件)级别,当前文件(suite)下所有case前后执行一次
      def setup_module():
          print("\n连接资源")
      
      def teardown_module():
          print("\n释放资源...")
      
      # 函数级别,每个函数case执行前后
      def setup_function():
          print("\n函数资源准备")
      
      def teardown_function():
          print("\n函数资源释放...")
      
      def inc(x):
          return x+1
      
      def test_assert1():
          assert inc(4) == 5
      
      def test_assert2():
          assert inc(3) == 4
      
      class TestAnswer():
          # 类级别,只在所有类方法前后执行一次
          def setup_class(self):
              print("\nsetup class")
      
          def teardown_class(self):
              print("\nteardown class...")
      
          def setup_method(self):
              print("\nclass method setup")
      
          def teardown_method(self):
              print("\nclass method teardown...")
      
          def test_demo1(self):
              assert 1==1
      
          def test_demo2(self):
              assert 2==2
      

Mark

  • 一个module(测试文件)里可能需要一部分不运行,或只运行某些case
  • 命令行一个个指定太麻烦,可以在编写case时加上标记
    • 比如某些case只在Web测,有些只在APP测
  • 自定义marker,标记测试用例:@pytest.mark.xxx
    # 模块(文件)级别,当前文件(suite)下所有case前后执行一次
    import pytest
    	
    def setup_module():
        print("\n连接资源")
    
    def teardown_module():
        print("\n释放资源...")
    
    # 函数级别,每个函数case执行前后
    def setup_function():
        print("\n函数资源准备")
    
    def teardown_function():
        print("\n函数资源释放...")
    
    def inc(x):
        return x+1
    
    @pytest.mark.integer
    def test_assert1():
        print("test print with -s param")
        assert inc(4) == 5
    
    @pytest.mark.integer
    def test_assert2():
        assert inc(3) == 4
    
    class TestAnswer():
        # 类级别,只在所有类方法前后执行一次
        def setup_class(self):
            print("\nsetup class")
    
        def teardown_class(self):
            print("\nteardown class...")
    
        def setup_method(self):
            print("\nclass method setup")
    
        def teardown_method(self):
            print("\nclass method teardown...")
    
        @pytest.mark.char
        def test_demo1(self):
            assert 1==1
    
        @pytest.mark.char
        def test_demo2(self):
            assert 2==2
    
  • 运行:pytest test_demo1.py -vs -m "integer"
    • 或者 pytest .\test_demo1.py -vs -m "not char"
  • 但我们发现有很多warning,因为pytest定义好了一些marker(比如skip),不用它的就会警告
    • 怎么不报警呢?新建 pytest.ini 文件
      [pytest]
      markers = integer
          char
      
    • 让pytest接受我们自定义的marker

skip

  • 跳过某些case的另一种方法,也支持条件过滤
    import sys
    import pytest
    
    @pytest.mark.skip
    def test_demo1():
        print("skip this case")
        assert True
    
    @pytest.mark.skip(reason="开发还没写代码")
    def test_demo2():
        assert False
    
    def check():
        return False
    
    # 测试代码里不满足某个条件,直接跳过,有点像skipif
    def test_demo3():
        print("test skip")
        if not check():
            pytest.skip("unsupported")
        print("end")
    
    @pytest.mark.skipif(sys.platform=="win32", reason="this kind of platform is not supported!")
    def test_demo4():
        assert True
    
  • mark.xfail,如果case执行成功则XPASS,如果失败则标记为XFAIL,主要是提示的作用,表示这里有个bug还没解决,我们后续可以通过 pytest test_demo1.py -vs -m "xfail" 执行这部分
    @pytest.mark.xfail
    def test_fail1():
        assert False
    
    xfail = pytest.mark.xfail # 定义装饰器
    
    @xfail
    def test_fail2():
        assert True
    
    def test_fail3():
        print("start test")
        pytest.xfail("功能代码未实现,失败") # 直接让case失败在这里,类似skip
        print("end")
    

参数化

  • 参数化设计方法就是将模型中的定量信息变量化,使之成为可以任意调整的参数
  • 比如要测试搜索框,搜索内容应该可传参,可以参数化;类似unittest + ddt
  • 单参数和多参数
    import pytest
    
    search_name = ['selenium', 'appium', 'ut', 'pytest']
    
    @pytest.mark.parametrize('name', search_name) # 4个case,取决于参数个数
    def test_param1(name):
        assert name in search_name
    
    @pytest.mark.parametrize("_input, expected", [('3+5', 9), ('4+4', 8)])
    def test_param2(_input, expected):
        assert eval(_input)+1 == expected
    
  • ids参数指定case名字,默认是你写的case名称,再拼上参数,参数之间用-连接,
    1
  • 笛卡尔积,用的比较少
    @pytest.mark.parametrize('p1', ['1','2','3'])
    @pytest.mark.parametrize('p2', ['4','5','6'])
    def test_param3(p1, p2):
        print(p1, "===", p2)
    
    2
  • 补充:命令行参数
    • --lf,即 --last-failed,只重新运行失败的cass,(为啥不是 --of)
    • --ff,即 --failed-first,先执行上次失败的case,再执行其他测试
      3
  • Python命令直接执行pytest测试
    • 命令:python test_demo1.py
      # test_demo1.py
      # 模块(文件)级别,当前文件(suite)下所有case前后执行一次
      import pytest
      
      def setup_module():
          print("\n连接资源")
      
      def teardown_module():
          print("\n释放资源...")
      
      # 函数级别,每个函数case执行前后
      def setup_function():
          print("\n函数资源准备")
      
      def teardown_function():
          print("\n函数资源释放...")
      
      def inc(x):
          return x+1
      
      @pytest.mark.integer
      def test_assert1():
          print("test print with -s param")
          assert inc(4) == 5
      
      @pytest.mark.integer
      def test_assert2():
          assert inc(3) == 4
      
      class TestAnswer():
          # 类级别,只在所有类方法前后执行一次
          def setup_class(self):
              print("\nsetup class")
      
          def teardown_class(self):
              print("\nteardown class...")
      
          def setup_method(self):
              print("\nclass method setup")
      
          def teardown_method(self):
              print("\nclass method teardown...")
      
          @pytest.mark.char
          def test_demo1(self):
              assert 1==2
      
          @pytest.mark.char
          def test_demo2(self):
              assert 2==2
      
      # python test_demo1.py
      if __name__ == '__main__':
          # pytest.main()   # 执行当前目录下的所有case,不只是这个文件
          # 传参,指定case
          # pytest.main(['test_demo1.py::TestAnswer::test_demo2', '-v'])
          # 指定Mark
          pytest.main(['test_demo1.py', '-v', '-m', 'char'])
      
    • 主要是为了避免后期在shell脚本同时使用python和pytest命令

异常处理

  • 第一种方式就是try...except...
    1
  • 第二种方式,pytest封装了raises
    def test_raises():
    	# 期望是Value异常
        with pytest.raises(ValueError) as exp:
            # 这里面就是我们测试功能的代码,比如用户输入非法值,看是不是我们期望的异常
            raise ZeroDivisionError('Value must gt 18') # 假装抛出异常,这种情况,case就会fail
        # assert exp.type is ValueError # 这两句没必要写,逻辑冗余
        # assert exp.value.args[0] == 'Value must gt 18'
    

数据驱动

  • pytest结合数据驱动测试(DDT)
  • 通过参数化直接实现DDT,demo
    • 配置文件,./env.yaml
      test: 127.0.0.1
      
    • case
      import pytest
      import yaml
      
      class TestDemo:
          @pytest.mark.parametrize('env', yaml.safe_load(open('./env.yml')))
          def test_demo1(self, env):
              if 'test' in env:
                  print("测试环境")
                  print(env)  # test, 只能打印出key
              elif 'dev' in env:
                  print("开发环境")
      
    • 如果想打印出全部信息,需要修改yml的写法
      -
        test: 127.0.0.1
        t2: 10086
      
      -
        t3: 10010
      
    • 从结果可以看出,yml文件的所有信息作为一个参数列表,一个 - 容纳一个字典,作为列表的一个元素,对应一个case
      class TestDemo:
          @pytest.mark.parametrize('env', yaml.safe_load(open('./env.yml')))
          def test_demo1(self, env):
              if 'test' in env:
                  print("测试环境")
                  print(env)  # {'test': '127.0.0.1', 't2': 10086}
                  print(env['test']) # 127.0.0.1
              elif 'dev' in env:
                  print("开发环境")
      
          def test_yml(self):
              # [{'test': '127.0.0.1', 't2': 10086}, {'t3': 10010}]
              print("\n", yaml.safe_load(open('./env.yml')))
      
    • 这里只是做个准备,一般不直接写参数
  • 通过pytest(参数化) + Excel实现DDT
    • 安装工具:pip install openpyxl,及基本用法
      import openpyxl
      
      # 打开工作簿
      book = openpyxl.load_workbook('./test.xlsx')
      # 读取工作表
      sheet = book.active
      # 读取单元格
      c1 = sheet['A2']    # Cell 对象
      c2 = sheet.cell(column=1, row=3)
      # 读取一片
      c3 = sheet['A1':'C3']
      # 获取单元格的值
      print(c1) # roy
      print(c3[0][0].value)   # Name
      
    • 准备数据,传给case,驱动测试;当然,离不开参数化
      1
      2
    • 注意目录结构
  • 通过pytest + CSV/JSON实现DDT
    • csv的特点是以逗号/制表符分隔字段,纯文本形式,可以直接用with open打开,Excel可直接改为CSV文件
    • json的特点是由嵌套的键值对组成,值的形式多样,可以是字符串、数组,内置json包
    • 文件操作可以搜一搜看一看,枯燥;其他和上面Excel一样,不赘述
  • 以上这部分是 pytest 基操,后续还有很多补充

Allure

  • 使用Allure定制测试报告
  • 优点:
    1
  • 官网
  • 文档
  • 安装
    • Java环境(建议1.8,但我是Java17)
    • 安装Allure(建议2.13),下载
  • Allure支持多语言是因为它基于XUnit开发的
    • xUnit中的 x 代表不同语言,Java就是JUnit,python就是unittest

集成

  • 测试和报告是不分家的,虽然Allure是个独立的报告框架,但还是要集成到不同语言的测试框架中使用
  • 集成到pytest
    • 安装:pip install allure-pytest
  • 常用方法
    • 一般以装饰器形式使用
      2
  • demo-title
    • 新建result目录,存放临时结果
      import allure
      
      class TestSearch:
          @allure.title("搜索:测试")
          def test_demo1(self):
              print("demo1")
      
    • 命令行:pytest test_allure.py --alluredir ./result --clean-alluredir,pytest --help
    • 查看报告:allure serve ./result
      3
  • demo-feature/story
    • 测试框架不同,但一般我们称一个测试文件为 suite(或者说module),suite里面可以有多个类,称为 case(或feature/TestCase),每个case里面又可以包含多个具体的用例,称为 story,story有时还可进一步分为多个 keyword
    • 也就是:suites > suite > case/feature/TestCase > story/keyword,有些框架 TestCase 下面就是keyword,本质是看谁在安排具体的测试过程,pytest中是story;不必纠结这个,理清层次就行
      import allure
      
      @allure.feature("登录模块")
      class TestLogin:
          # 不加说明会有warning
          @allure.story("登录成功")
          def test_login_success(self):
              print("success")
      
          @allure.story("登录失败")
          def test_login_fail(self):
              print("fail")
      
    • 可以在下方看到指定的 features;SUITES包含所有suite
      4
      5
    • feature也可以作为Marker:pytest .\test_feature.py --allure-features="登录模块" --alluredir=./result --clean- alluredir 只运行这个feature的story
    • 同样的,--allure-stories 指定跑哪些story
  • demo-step
    • 给story下面的步骤划分step,一般以页面切换为分割点
      @allure.feature("登录模块")
      class TestLogin:
          # 不加说明会有warning
          @allure.story("登录成功")
          @allure.title("fail")
          def test_login_success(self):
              with allure.step("1. 打开登录界面"):
                  print("login page")
                  print("输入用户名密码...")
              with allure.step("2. 跳转到首页"):
                  print("首页...")
      
    • 会分开展现
      6
  • demo-link
    • 使用链接的方法有多种,包括 link/issue/testcase
    • pytest test_link.py --alluredir ./result --allure-link-pattern=issue:http://www.bug-platform.com/{} --clean-alluredir
      TEST_CASE_LINK = 'https://github.com/qameta/allure-integrations/issues/8#issuecomment-268313637'
      
      # 链接 + 名称
      @allure.link('https://www.youtube.com/watch?v=Su5p2TqZxKU', name='Click me')
      def test_with_named_link():
          pass
      
      # 140这个位置一般是bug号,可以接入自己公司的bug平台,命令行要配置:
      # pytest directory_with_tests/ --alluredir=/tmp/my_allure_report --allure-link-pattern=issue:http://www.myself-bug-platform.com/issue/{}
      @allure.issue('140', 'Pytest-flaky test retries shows like test steps')
      def test_with_issue_link():
          pass
      
      # 超链接到上面的link, 看起来和link好像没什么区别
      @allure.testcase(TEST_CASE_LINK, 'Test case title')
      def test_with_testcase_link():
          pass
      
      7
  • demo-级别
    • 有五种级别可以设置
      8
    • 设置了severity,也相当于设置了一个Marker,跑指定级别的case:pytest .\test_severity.py --allure-severities=blocker,trivial --alluredir=./result
      import allure
      
      def test_with_no_severity_label():
          pass
      
      # Blocker
      @allure.severity(allure.severity_level.BLOCKER)
      def test_with_blocker_severity_label():
          assert 1==2
      
      @allure.severity(allure.severity_level.TRIVIAL)
      def test_with_trivial_severity():
          assert 2==4
      
      @allure.severity(allure.severity_level.NORMAL)
      def test_with_normal_severity():
          pass
      
      @allure.severity(allure.severity_level.NORMAL)
      class TestClassWithNormalSeverity(object):
      
          def test_inside_the_normal_severity_test_class(self):
              pass
      
          @allure.severity(allure.severity_level.CRITICAL)
          def test_inside_the_normal_severity_test_class_with_overriding_critical_severity(self):
              pass
      
      8
  • demo-添加附件
    • 附件的类型有很多种
      class TestLogin:
          def test_login_success(self):
              with allure.step("1. 打开登录界面"):
                  print("login page")
                  allure.attach.file("./sisi.jpg", name="wechat", attachment_type=allure.attachment_type.JPG)
              with allure.step("2. 跳转到首页"):
                  print("首页...")
      
    • 比如添加个图片
      9

生成报告

  • 上面使用 allure serve 命令得到在线报告,其实测试报告的生成有完整流程
    1
  • 最终版本的测试报告,意思就是不依赖IDE的allure进程,可以移植到自己搭建的服务器或者Jenkins
    • 生成报告:allure generate ./result
    • 打开:allure open -h 127.0.0.1 -p 8883 ./allure-report,或者在IDE直接打开 index.html,但不能在文件夹直接打开(需要服务器解析,不是静态文件)
  • 具体用法在学到Jenkins就知道了

Fixture

  • Fixture :固定装置
  • pytest提供的装饰器,可以更加灵活的安排用例的执行、需要的前置/后置操作等
  • 官方文档,参数及许多内置的 fixture 都可以找到

基操

  • 比如有些用例的执行不需要登录,有些需要;使用 setup 就不行,逐个在用例里 login 太繁琐
    import pytest
    
    @pytest.fixture
    def login():
        print("\n登录成功")
    
    # 需要登录,传入被fixture的函数即可
    def test_card(login):
        print("加入购物车成功")
    
  • 相当于随时随地 setup,文明又卫生

作用域

  • 类似setup、setup_module 等,在这个作用域里都要执行某个 Fixture
  • 主要分为这几个 scope,可以到源码里看注释
    1
  • 函数级别,注意:还是要在具体函数里面调用 login
    @pytest.fixture(scope="function")
    def login():
        print("\n登录成功")
    
    def test_card(login):
        print("加入购物车成功")
    
    def test_search(login):
        print("搜索商品")
    
  • 模块级别,在所有用例之前执行一次,类似 setup_module
    @pytest.fixture(scope="module")
    def login():
        print("\n登录成功")
    
    def test_card(login):
        print("加入购物车成功")
    
    def test_search(login):
        print("搜索商品")
    
  • class级别,注意:这里的每个函数也被当做类
    @pytest.fixture(scope="class")
    def login():
        print("\n登录成功")
    
    def test_card(login):
        print("加入购物车成功")
    
    def test_search(login):
        print("搜索商品")
    
    class TestClass:
        def test_demo1(self, login):
            print("class 1")
    
        def test_demo2(self, login):
            print("class 2")
    
    2
  • 看起来就是 setup 那些情况呀?好在哪?其实就灵活在每个函数都要写 login,控制了哪些能执行,有点像 Mark 了;麻烦了点但灵活了,祸兮福之所倚

yield

  • 这个是python的关键字,主要是生成器用,实现懒加载节省内存;特点是控制了代码的执行流程,yield 这里直接返回,但还能回来接着执行后续代码
    @pytest.fixture(scope="class")
    def login():
        print("\n登录成功")
        yield
        print("\n登出")
    
    def test_card(login):
        print("加入购物车成功")
    
    # 登录成功 加入购物车成功 登出
    
  • 这就类似 teardown 操作

数据共享

  • 不需要 import,就可以用一些公共的模块,也可以限制共享的区域
  • 新建 conftest.py,名字不能变,放在哪个位置,哪个目录下面的用例就可以共享这个 fixture
  • demo,顺便测一下 session 级别(一般也就是这么用,session和conftest一起)
    import pytest
    
    @pytest.fixture(scope="session")
    def login():
        print("\n登录成功")
        yield
        print("\n登出")
    
    1
  • conftest.py 所在的目录视为一个session 域,不管下面有多少用例引用,只执行一次

自动应用

  • 不在用例中传 fixture,也想自动引用
    import pytest
    
    # 设置 autouse
    @pytest.fixture(scope="function", autouse=True)
    def login():
        print("\n登录成功")
        yield
        print("\n登出")
    
    # 不需要写 login,也能使用
    def test_conf_1():
        print("测试 conftest")
    

参数化

  • 和 pytest 参数化类似
  • demo,同样的,有几个参数就会复制几个 case
    import pytest
    
    @pytest.fixture(scope="session", params=["roy", "allen"])
    def parameter(request):
        print(f"this is {request.param}")
        yield request.param # 返回参数
        print("baibai %s"%request.param)
    
    def test_conf_1(parameter):
        print("\nfixture parameters test")
        print("参数为:", parameter)
    
    def test_conf_2(parameter):
        print("\n参数化")
        print("参数为:", parameter)
    
  • 虽然设置了 session 级别,但这里也相当于在 session 级别复制出两份 case,可以理解为两个会话了,在各自的会话里 consume 参数;级别和参数化并不冲突,一个参数一个域,分开看即可
  • 这部分更多是属于逻辑层次问题,还是要多试验,才能更得心应手,死记硬背容易搞复杂

ini

  • pytest.ini 文件,是 pytest 的配置文件,可以修改 pytest 的默认行为,不能包含任何中文(Windows)
  • 主要配置以下几项

运行规则

  • 项目根目录下新建 pytest.ini 文件
  • 执行 check_ 开头和 test_ 开头的测试文件(suite/module),要加 *
    ;这是个注释,以分号开头,但是Windows下不能有中文
    python_files = check_* test_*
    
  • 执行 CheckTest 开头的类(case)
    python_classes = Test* Check*
    
  • 执行 check_ 开头和 test_ 开头的方法(story)
    python_functions = check_* test_*
    

配置命令行参数

  • 命令行参数,一般用等号赋值
    ;就不用手动添加了
    addopts = -vs --alluredir=./result
    

指定/忽略执行目录

  • 设置执行路径
    testpaths = demo demo3
    
    • 忽略某些路径
    norecursedirs = result md2
    

配置日志

  • 日志开关,设置日志级别,打印,保存位置
    1
  • 文件记得提前创建,或者加判断

插件开发

  • 插件分类
    • 外部插件:pip install 安装的
    • 本地插件:自己编写,通过 pytest 自动模块发现机制使用(conftest.py)
    • 内置插件:代码内部的 _pytest 目录加载(hook函数)
  • 没啥神奇的,就是一些封装好的方便测试的代码

常用插件

  • 可以到这里找
  • 举例:pytest-ordering
    • 安装:pip install pytest-ordering
    • 用法:@pytest.mark.run(order=2),也可以看源码,用 first/second 等代替
    • 多个装饰器同时使用可能会有冲突
    • 对于顺序,有时不应该刻意定义
  • 常见的插件
    1

分布式并发

  • 分布式:多台机器同时执行缩短耗时,也可称为并行;机器数 * 内核数 = 并行进程个数
  • 并发:多个进程同时操作同一批数据,要避免弄脏数据,或者说实现高并发
  • 解决这两个问题:pytest-xdist,可以去官网搜搜看
  • 安装:pip install pytest-xdist
  • 使用:pytest -n autopytest -n NUMCPUS 即内核数

自定义插件

  • 这里要用到下面的 hook 函数部分
  • 插件1:修改默认编码
    • 在 conftest.py 中使用 hook 函数
      def pytest_collection_modifyitems(
          session: "Session", config: "Config", items: List["Item"]
      ) -> None:
          print(items)
          # 单步调试可以发现:我们需要改每个 item(用例) 的 name 和 nodeID 两个编码
          for item in items:
              item.name = item.name.encode('utf-8').decode('unicode-escape')
              item._nodeid = item.nodeid.encode('utf-8').decode('unicode-escape')
      
    • 运行测试用例,会自动调用钩子函数修改编码
      1
  • 插件2:添加命令行参数
    • 在 conftest.py 定义
      # hook 函数,添加命令行参数
      def pytest_addoption(parser):
          mygroup = parser.getgroup("Roy") # 参数组
          mygroup.addoption("--env", default='allen', dest='env', help='set your env')
      
      # 用 fixture 过滤参数
      @pytest.fixture(scope='session')
      def cmdoption(request):
          myenv = request.config.getoption("--env", default='allen')
          if myenv == 'roy':
              datapath = "datas/roy/data.yml"
          elif myenv == 'allen':
              datapath = "datas/allen/data.yml"
          else:
              datapath = "datas/data.yml"
      
          with open(datapath) as f:
              data = yaml.safe_load(f)
          return myenv, data
      
    • 数据格式
      env:
        ip: 127.0.0.1
        port: 8999
      
    • 传入 fixture,过滤参数
      5
    • 命令行调用 pytest --env 'roy' .\test_conf.py -vs,能看到对应输出
    • 使用 pytest --help 也能看到参数介绍

打包发布

  • 想让我们自定义的插件给别人用,有几种方式
    • 源代码提交到 git
    • 打包项目
  • 具体看打包项目的方式,就是借用 python 的打包工具
    • 需要准备:源码包,pyproject.toml,测试包
    • 需要安装:pip install setuptool pip install wheel,一个是打包的,一个是压缩的
    • 打包命令:python -m build
    • 发布到 PyPI:需要用到 twine 工具;都是参考上面那个教程,英文的慢慢看

hook

  • hook:钩子,即在需要的时候挂一个东西上去;在pytest中
    • 是个函数,被系统调用(系统消息触发),在不同阶段实现不同功能
    • 自动触发机制
    • hook函数的名称固定
    • pytest 有非常多的 hook 函数,在跑一个 case 的时候,会经历下面的过程:
      1
    • 总结如下
      3
  • 官方文档
    • which can be implemented by conftest.py files and plugins
    • 参考文档
  • 上面的 hook 函数大部分只有一个“影子”(只定义了方法名),我们可以做具体实现(implement);有些必要的会自动实现(挂钩)
    • 这怎么感觉有点像 Java 的 interface
    • 名称定义在 site-packages/_pytest/hookspec.py 文件中,pip 安装的包都放在 site-packages
      2
    • 那就在 conftest.py 中实现两个试试
      4
    • 这两个有点像 suite 中定义的 setup/teardown,底层应该是一样的
  • hook 是一种编程机制,和具体的语言没有直接的关系
    • hook 又和回调函数相类似,参考文章
    • 这种设计模式实现起来并不复杂,关键在于定义注册函数,并合理消费被注册的hook函数

小结

  • 以上就是 pytest 框架及相关的知识点
  • 框架的定位和使用技巧还是要在实践中体会,何时用,怎么用合适才是功力

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/57937.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SAS,Stata,HLM,R,SPSS和Mplus分层线性模型HLM分析学生受欢迎程度数据

全文链接:http://tecdat.cn/?p10809本文用于比较六个不同统计软件程序(SAS,Stata,HLM,R,SPSS和Mplus)的两级分层线性模型的过程和输出(点击文末“阅读原文”获取完整代码数据&#…

Java项目:SSM学生会管理系统

作者主页:源码空间站2022 简介:Java领域优质创作者、Java项目、学习资料、技术互助 文末获取源码 项目介绍 本项目分为管理员、学生两种角色, 管理员角色包含以下功能: 管理员登陆,管理学生,管理机构,活动信息发布,部门管理,职位…

仿SpringBoot启动Dome实现

文章目录前言环境搭建依赖项目结构实现启动获取服务器自动配置启动Tomcat总结前言 填一下以前这篇博文:如何纯注解整合Spring SpringMVC Mybatis埋下的坑,我们来简单的了解一下SpringBoot它做的一些自动配置是怎么一回事,同时也看看SpringBo…

【人民币识别】人民币序列号识别【含GUI Matlab源码 908期】

⛄一、简介 本文描述的人民币序列号识别系统实现了从图像预处理到识别结果的过程, 而序列号识别是本文的重要内容.以序列号区域为研究对象, 主要包括图像预处理、图像分割以及序列号识别等过程。 1 图像预处理 人民币图像总体上来说灰度偏高, 灰度值基本上都大于150 (对8位25…

具有平滑正曲线边界的一般凸体的精确闭式闵可夫斯基研究(Matlab代码实现)

👨‍🎓个人主页:研学社的博客 💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜…

Minecraft 1.19.2 Forge模组开发 04.动画效果物品

我们本次实现一个具有动画效果的物品,本次演示的模型代码均在文末给出 效果演示效果演示效果演示 首先,请确保你的开发包中引入了geckolib依赖,相关教程请参考:Minecraft 1.19.2 Forge模组开发 03.动画生物实体 1.首先我们要使用geckolib制…

(三)操作系统的运行环境

文章目录一、操作系统的运行机制1. 时钟管理2. 中断机制3. 原语4. 系统数据结构5. 系统调用二、操作系统体系结构1. 传统的操作系统结构(大内核)第一代:无结构OS第二代:模块化结构OS:模块-接口法OS第三代:分…

[附源码]计算机毕业设计springboot学习帮扶网站设计与实现

项目运行 环境配置: Jdk1.8 Tomcat7.0 Mysql HBuilderX(Webstorm也行) Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。 项目技术: SSM mybatis Maven Vue 等等组成,B/S模式 M…

坦克大战②

1. 我方坦克发射单颗子弹 当发射一颗子弹后,就相当于启动一个线程来控制它的位置坐标;Hero[我方坦克]有子弹的对象,当按下J时,就创建一个发射子弹的线程,通过坐标变化让子弹不停的移动,形成一个射击的效果&…

redis 集群搭建的三种方式

文章目录一、Redis主从二、Redis哨兵三、Redis集群一、Redis主从 二、Redis哨兵 三、Redis集群 下载redis wget http://download.redis.io/releases/redis-5.0.3.tar.gz解压redis tar zxvf redis-5.0.3.tar.gz进行重命名 mv redis-5.0.3 redis安装gcc yum install gcc进入red…

如何学习一门技术

如何学习一门技术 同样的生活,在你经历了一些意外和不如意之后,你再回过头来看,之前你所抱怨的生活其实是一个蛮不错的生活。 罗翔:每一个人都应该拥有学习的能力和权力,真正的知识要能走出书斋,去影响每…

StarkNet 性能路线图

目录 前言 区块限制:Validity Rollups vs L1 为什么 L1 吞吐量有限? 为什么相同的障碍不影响validity rollups? Sequencer 并行化 Cairo-VM 的新 Rust 实现 Rust 对 sequencer重新实现 Provers呢? Summary 参考 前言 St…

详解设计模式:迭代器模式

迭代器模式(Iterator Pattern)也被称为游标模式(Cursor Pattern),是在 GoF 23 种设计模式中定义了的行为型模式。是一种最简单也最常见的设计模式。 迭代器模式 可以让用户透过特定的接口巡访容器中的每一个元素而不用…

HTML5期末大作业:美食网页主题网站设计与实现——HTML+CSS+JavaScript月饼美食食品企业网站html模板9页面

👨‍🎓静态网站的编写主要是用HTML DIVCSS JS等来完成页面的排版设计👩‍🎓,常用的网页设计软件有Dreamweaver、EditPlus、HBuilderX、VScode 、Webstorm、Animate等等,用的最多的还是DW,当然不同软件写出的…

[附源码]计算机毕业设计校园运动会管理系统Springboot程序

项目运行 环境配置: Jdk1.8 Tomcat7.0 Mysql HBuilderX(Webstorm也行) Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。 项目技术: SSM mybatis Maven Vue 等等组成,B/S模式 M…

Linux基本工具——vim

Linux编辑器vim什么是vimvim的三种常用模式vim的基本操作命令模式插入模式底行模式搭配vim环境sudo怎么才能让普通用户使用什么是vim vim是linux下一款功能强大,多模式的编辑器。 现阶段有13种模式。 这就是进入vim的方式。 vim的三种常用模式 命令模式 我们第一…

CentOS7 编译安装最新的Linux Kernel 6.0 rc3

哪个男孩不想手动编译一份自己的内核呢?安装编译环境 CentOS7安装必要的包 yum groupinstall "Development Tools" -y && yum install openssl-devel -y && yum install rpm-build redhat-rpm-config asciidoc hmaccalc perl-ExtUtils-…

Java核心技术卷Ⅰ-第四章对象和类

重点 1.使用预定义类 2.方法参数 3.对象构造 4.包 5.类设计技巧 1.使用预定义类 一个源文件只能有一个公共类,可以有任意数目的非公共类可以使用通配符调用Java编译器:javac Test*.java使用var声明局部变量就不用担心0、0L和0.0之间的区别,因…

找出链表中间结点的三种解法

初阶链表刷题注意!!!学习的是解题的思维! 找出链表的中间结点(链接在末尾) 解题思路 数组解法 由于链表不能通过下标访问对应的结点,所以我们将所有的结点存储在数组中,这样就可以通…

测试开发怎么学?

随着互联网行业的高速发展,快速高质量的产品版本迭代成为企业始终立于不败之地的迫切需求,而在短期迭代的快节奏中.传统测试工作面对更大压力,无法持续提供高效率高质量的人力支撑,所以越来越多的企业需要技术更为全面的测试开发工程师。 测试开发本质上属于测试,区…