pytest-学习过程笔记

Pytest

一、Pytest 简介

1、Pytest是一个基于Python的单元测试框架,同类型的还有比如Unittest

2、Pytest能够结合requests、selenium、appium进行集成自动化测试和UI自动测试

3、Pytest能够搭配Allure生成高质量的测试报告,也能够通过Jenkins去做持续集成

4、支持多种插件,我们可以自己去做插件开发

5、简单灵活

6、兼容unittest,nose等单测框架

7、unittest与pytest的对比

  • ========== unittest==================== pytest
    1、用例编写规则 1、测试文件必须先import unittest
    2、测试类必须继承unittest.TestCase
    3、测试方法必须以“test_”开头
    4、测试类必须要有unittest.main()方法
    1、测试文件名/方法/类名可以以“test_”开头或者"_test"结尾(如:test_ab.py),也可以通过ini文件进行自定义。
    2、用例分类执行 默认执行全部用例,也可以通过加载testsuit,执行部分用例 可以通过@pytest.mark来标记类和方法,pytest.main加入参数("-m")可以只运行标记的类和方法
    4、用例前置和后置 提供了setUp/tearDown,只能针对所有用例 pytest中的fixture显然更加灵活。可以任意自定义方法函数,只要加上@pytest.fixture()这个装饰器,那么被装饰的方法就可以被使用
    4、参数化 需依赖ddt库 使用@pytest.mark.parametrize装饰器
    5、断言 很多断言格式(assertEqual、assertIn、assertTrue、assertFalse) 只有assert一个表达式,用起来比较方便
    6、报告 使用HTMLTestRunnerNew库 有pytest-HTML、allure插件
    7、失败重跑 无此功能 pytest支持用例执行失败重跑,pytest-rerunfailures插件

二、Pytest 安装与设置

1、安装:存在python 3.6及以上的环境,设置好环境变量的前提下,通过命令行执行:pip install [-U] pytest进行安装。

-U的作用是当已经存在pytest包的时候,且有新的pytest版本时,可以对它进行一个更新。

2、pycharm设置:settings中搜索pytest,然后选择tools/Python integrated Tools,在default test runner中选择pytest作为执行测试用例的一个默认解释器。(默认是unittest,改下就好)

三、Pytest 第一个demo

  • 官网:Get Started — pytest documentation

  • 官网示例:

    # 被测函数:对于输入数字进行+1操作
    def inc(x):
        return x + 1
    
    
    # 基于Pytest,对被测函数进行断言
    def test_answer():
        assert inc(3) == 5
    

    执行方式:使用命令行进入对应目录,输入pytest(windows系统可以使用git bash模拟linux命令行操作)

      $ pytest
      =========================== test session starts ============================
      platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y
      rootdir: /home/sweet/project
      collected 1 item
    
      test_sample.py F                                                     [100%]
    
      ================================= FAILURES =================================
      _______________________________ test_answer ________________________________
    
          def test_answer():
      >       assert inc(3) == 5
      E       assert 4 == 5
      E        +  where 4 = inc(3)
    
      test_sample.py:6: AssertionError
      ========================= short test summary info ==========================
      FAILED test_sample.py::test_answer - assert 4 == 5
      ============================ 1 failed in 0.12s =============================
    

四、Pytest 用例格式要求

文件:test_开头 or _test结尾

类:Test开头

方法/函数:test_开头

【注】:测试类中不可以添加__init__构造函数

一般性用例结构:

class Testxxx

setup

资源准备

teardown

资源销毁

test_xxx

测试用例

断言:

assert <表达式> [,断言描述]

表达式可以是左边== or != 右边,也可以是左边 in or not in 右边

五、Pytest 测试框架结构

测试装置介绍:

类型 规则
setup_module/teardown_module 全局模块级(一个Python文件)
setup_class/teardown_class 类级别,只在类中前后运行一次
setup_function/teardown_function 函数级,在类外
setup_method/teardown_method 方法级,类中的每个方法执行前后
setup/teardown 在类中,运行在调用方法的前后
def setup_module():
    print("开始测试,执行模块级别的setup")


class TestFunc001:

    def test_func_001(self):
        print("测试函数1")

    def test_func_002(self):
        print("测试函数2")

    @staticmethod
    def setup():
        print("开始测试,执行方法级别的setup")

    @staticmethod
    def setup_function():
        print("开始测试,执行函数级别的setup")

    @staticmethod
    def setup_class():
        print("开始测试,执行类级别的setup")


class TestFunc002:

    def test_func_001(self):
        print("测试函数1")

    def test_func_002(self):
        print("测试函数2")

    @staticmethod
    def setup():
        print("开始测试,执行方法级别的setup")

    @staticmethod
    def setup_function():
        print("开始测试,执行函数级别的setup")

    @staticmethod
    def setup_class():
        print("开始测试,执行类级别的setup")

六、Pytest 参数化

官方文档:How to parametrize fixtures and test functions — pytest documentation

定量信息变量化,使之成为任意调整的参数

最简单的一个例子,测试登录功能,使用参数化,就可以只编写一个用例模板,然后准备好参数(各种登录名和密码以及对应的响应的组合),喂给模板,就可以实现所有的场景的测试

  • 单参数

    import pytest
    
    search_list = ["aaa", "bbb", "ccc"]
    
    
    # pytest.mark.parametrize装饰器第一个参数要和被测用例的参数一致
    # pytest.mark.parametrize装饰器第二个参数传一个列表,每一个值,代表一个数据
    # pytest.mark.parametrize装饰器第三个参数,选传,传用例名称
    @pytest.mark.parametrize("key_word", ["aaa", "d", "f"], ids=["测试aaa", "测试d", "测试f"])
    def test_search(key_word):
        assert key_word in search_list
    
    
  • 多参数

    import pytest
    
    # 第一个参数要和被测用例的参数一致
    # 第二个参数传一个列表,每一个值需要用可迭代对象比如元祖包裹且元祖内数据量与参数数量相同,代表一个数据
    @pytest.mark.parametrize("test_input,expected", [("3+5", 8), ("2+4", 6), ("6*9", 42)])
    def test_eval(test_input, expected):
        assert eval(test_input) == expected
    
  • 用例重命名

    默认参数的名字,通过属性ids进行修改

  • 笛卡尔积

    堆叠@pytest.mark.parametrize这个装饰器,排列组合产生多组数据

    import pytest
    
    
    @pytest.mark.parametrize("x", [1, 2, 3])
    @pytest.mark.parametrize("y", ["第一组", "第二组"])
    def test_foo(x, y):
        print("{}第{}个数".format(y, x))
    

七、Pytest 用例标记

1、官方文档:How to mark test functions with attributes — pytest documentation

2、使用场景:只去执行某一部分特定用例。比如只执行【采购】模块的用例

3、使用方式:使用@pytest.mark.标签名装饰被测函数

4、执行方式:

pytest xxx.py -m 标记名

pytest xxx.py -m=标记名

pytest xxx.py -m “not 标记名”

5、自定义标签:

在测试文件中打上对应标签:

purchase:采购

sale:销售

import pytest


@pytest.mark.purchase
def test_purchase_001():
    print("采购用例1")


@pytest.mark.purchase
def test_purchase_002():
    print("采购用例2")


@pytest.mark.purchase
def test_purchase_003():
    print("采购用例3")


@pytest.mark.sale
def test_sale_001():
    print("销售用例1")


@pytest.mark.sale
def test_sale_002():
    print("销售用例2")


@pytest.mark.sale
def test_sale_003():
    print("销售用例3")

执行及结果:

命令行输入:pytest -vs test_mark.py -m purchase

(pytest_study_virtualenv) D:\PycharmProject\pytest_study>pytest test_mark.py -m purchase
================================================================================== test session starts ===================================================================================
platform win32 -- Python 3.7.4, pytest-7.1.2, pluggy-1.0.0
rootdir: D:\PycharmProject\pytest_study, configfile: pytest.ini
plugins: allure-pytest-2.9.45, encode-1.0.1, forked-1.4.0, ordering-0.6, xdist-2.5.0
collected 6 items / 3 deselected / 3 selected                                                                                                                                             

test_setup.py ...                                                                                                                                                                   [100%]

==================================================================================== warnings summary ====================================================================================
test_setup.py:4
  D:\PycharmProject\pytest_study\test_setup.py:4: PytestUnknownMarkWarning: Unknown pytest.mark.purchase - is this a typo?  You can register custom marks to avoid this warning - for detai
ls, see https://docs.pytest.org/en/stable/how-to/mark.html
    @pytest.mark.purchase

test_setup.py:9
  D:\PycharmProject\pytest_study\test_setup.py:9: PytestUnknownMarkWarning: Unknown pytest.mark.purchase - is this a typo?  You can register custom marks to avoid this warning - for detai
ls, see https://docs.pytest.org/en/stable/how-to/mark.html
    @pytest.mark.purchase

test_setup.py:14
  D:\PycharmProject\pytest_study\test_setup.py:14: PytestUnknownMarkWarning: Unknown pytest.mark.purchase - is this a typo?  You can register custom marks to avoid this warning - for deta
ils, see https://docs.pytest.org/en/stable/how-to/mark.html
    @pytest.mark.purchase

test_setup.py:19
  D:\PycharmProject\pytest_study\test_setup.py:19: PytestUnknownMarkWarning: Unknown pytest.mark.sale - is this a typo?  You can register custom marks to avoid this warning - for details,
 see https://docs.pytest.org/en/stable/how-to/mark.html
    @pytest.mark.sale

test_setup.py:24
  D:\PycharmProject\pytest_study\test_setup.py:24: PytestUnknownMarkWarning: Unknown pytest.mark.sale - is this a typo?  You can register custom marks to avoid this warning - for details,
 see https://docs.pytest.org/en/stable/how-to/mark.html
    @pytest.mark.sale

test_setup.py:29
  D:\PycharmProject\pytest_study\test_setup.py:29: PytestUnknownMarkWarning: Unknown pytest.mark.sale - is this a typo?  You can register custom marks to avoid this warning - for details,
 see https://docs.pytest.org/en/stable/how-to/mark.html
    @pytest.mark.sale

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
====================================================================== 3 passed, 3 deselected, 6 warnings in 0.02s =======================================================================


存在warnings信息:PytestUnknownMarkWarning: Unknown pytest.mark.purchase - is this a typo? You can register custom marks to avoid this warning - for detai
ls, see How to mark test functions with attributes — pytest documentation

在pytest.ini文件中做如下操作:

[pytest]
markers =
    purchase
    sale

八、Pytest设置跳过、预期失败用例

使用场景:希望跳过一下我们不想执行的用例

官方文档:https://docs.pytest.org/en/7.1.x/how-to/skipping.html

1、skip & skipif

方式一:装饰被测函数@pytest.mark.skip(reason="")

@pytest.mark.skip(reason="这条用例未编写完成")
def test_finance():
	pass

方式二:代码中添加pytest.skip(reason)

import sys
import pytest


if not sys.platform.startswith("win32"):
    pytest.skip("不是window平台,我们就不执行这个用例")

方式三:使用skipif

import sys
import pytest


@pytest.mark.skipif(sys.platform == "win32",reason="windows环境不执行这个用例")
def test_linux_case():
	print("非linux环境不执行此用例")

2、xfail

场景可以是标记某一个未解决的bug,可以通过此标签进行跟踪

如果这个bug未解决,然后运行时,执行结果会是xfailed,如果成功了,执行结果是xpassed,成功了,就可以把这个标签去掉了

import pytest


@pytest.mark.xfail
def test_function1():
    print("问题解决了!")
    assert 1 == 1


@pytest.mark.xfail
def test_function2():
    print("问题还没有解决!")
    assert 1 == 2
x问题还没有解决!

@pytest.mark.xfail
    def test_function2():
        print("问题还没有解决!")
>       assert 1 == 2
E       assert 1 == 2

test_setup.py:13: AssertionError
X问题解决了!
                                                         [100%]

======================== 1 xfailed, 1 xpassed in 0.03s ========================

九、Pytest 用例运行

运行 1个/多个 用例包/模块/类/方法

官方文档:How to invoke pytest — pytest documentation

1、收集当前路径下所有pytest能识别的用例: 命令行输入:pytest,

2、收集当前路径下存在的xxx.py文件中的所有pytest能识别的用例 命令行输入:pytest xxx.py

3、收集某个文件中某个类: 命令行输入:pytest xxx.py::类名

4、收集某个文件中某个类中某个方法: 命令行输入:pytest xxx.py::类名::方法名

十、Pytest 测试用例调度与运行

1、通过pytest命令行执行:

命令1:pytest --lf(–last-failed)只重新运行故障(第一次失败10条,成功100条。假设第二次–lf执行了失败的10条,然后10条全部成功了,这个时候再运行–lf,将执行110用例)

命令2:pytest --ff(–failed-first)先运行故障然后再运行其他用例

常见的命令行参数:

  • –help:获取所有参数列表

  • -x :用例一旦失败(fail/error),就立刻停止执行

  • –maxfail=num 用例达到num个失败,就停止执行

  • -m :标记用例

  • -k:执行用例名中包含某个关键字的测试用例

    # test_demo.py
    
    
    def test_haha():
    	pass
    
    
    def test_xixi():
        pass
    
    (pytest_study_virtualenv) D:\PycharmProject\pytest_study>pytest test_demo.py -vs -k "xixi"
    ================================================================================== test session starts ===================================================================================
    platform win32 -- Python 3.7.4, pytest-7.1.2, pluggy-1.0.0 -- d:\virtualenv\pytest_study_virtualenv\scripts\python.exe
    cachedir: .pytest_cache
    rootdir: D:\PycharmProject\pytest_study, configfile: pytest.ini
    plugins: allure-pytest-2.9.45, encode-1.0.1, forked-1.4.0, ordering-0.6, xdist-2.5.0
    collected 2 items / 1 deselected / 1 selected                                                                                                                                             
    
    test_setup.py::test_xixi PASSED
    
    ============================================================================ 1 passed, 1 deselected in 0.04s =============================================================================
    
    
    

    也可以加逻辑判断not进行执行:pytest test_demo.py -vs -k “not xixi”

  • -v 打印用例成功与否的详细的日志

  • -s:打印输出日志比如print的那些东西

  • –collect-only:只收集,不执行

2、通过python执行

  • 通过pytest.main()函数

    # main.py
    
    
    import pytest
    
    
    if __name__ == '__main__':
        pytest.main(["test_demo.py", "-vs", "-k", "not xixi"])
    
    ============================= test session starts =============================
    platform win32 -- Python 3.7.4, pytest-7.1.2, pluggy-1.0.0 -- D:\virtualenv\pytest_study_virtualenv\Scripts\python.exe
    cachedir: .pytest_cache
    rootdir: D:\PycharmProject\pytest_study, configfile: pytest.ini
    plugins: allure-pytest-2.9.45, encode-1.0.1, forked-1.4.0, ordering-0.6, xdist-2.5.0
    collecting ... collected 2 items / 1 deselected / 1 selected
    
    test_setup.py::test_haha PASSED
    
    ======================= 1 passed, 1 deselected in 0.01s =======================
    
  • 命令行输入:python -m pytest test_demo.py -vs -k “not xixi”

十一、Pytest 异常处理(没看)

十一、yaml(忘记了)

十二、 Pytest 结合Allure

官方文档:Allure Framework

安装:

  • Java语言开发,需要存在Java环境

  • 安装allure server:

    1. 下载:Central Repository: io/qameta/allure/allure-commandline
    2. 环境变量,加入到PATH中:D:\allure\allure-2.13.8\bin
    3. cmd:allure --version 验证安装成功
  • 安装python能识别的第三方库:

    pip install allure-pytest

这个模式类似于selenium?忘记了

执行:

  1. 通过pytest --alluredir命令生成中间结果
  2. 再通过:
    • allure serve 命令生成在线版本测试报告
    • allure generate命令生成最终版本测试报告

常用命令:

  • allure --help
  • 中间结果:pytest --alluredir=指定一个生成的中间结果的保存路径
  • 生成在线报告:allure server 指定一个生成的中间结果的保存路径
  • 生成报告:allure generate 指定一个生成的中间结果的保存路径 -o

1、test文件:

"""
=========================
File: test_allure.py
Author: dancing
Time: 2022/8/24
E-mail: 1@1.com
=========================
"""
import pytest


def test_one():
    assert 1 == 1


def test_two():
    assert 1 == 2

2、执行pytest test_allure.py --alluredir ./results_2命令,生成中间文件,生成的路径为./results_2

(pytest_study_virtualenv) D:\PycharmProject\pytest_study>pytest test_allure.py --alluredir ./results_2
================================================================================== test session starts ===================================================================================
platform win32 -- Python 3.7.4, pytest-7.1.2, pluggy-1.0.0
rootdir: D:\PycharmProject\pytest_study, configfile: pytest.ini
plugins: allure-pytest-2.9.45, encode-1.0.1, forked-1.4.0, ordering-0.6, xdist-2.5.0
collected 2 items                                                                                                                                                                         

test_allure.py F.                                                                                                                                                                   [100%]

======================================================================================== FAILURES ========================================================================================
________________________________________________________________________________________ test_two ________________________________________________________________________________________

    def test_two():
>       assert 1 == 2
E       assert 1 == 2

test_allure.py:17: AssertionError
================================================================================ short test summary info =================================================================================
FAILED test_allure.py::test_two - assert 1 == 2
============================================================================== 1 failed, 1 passed in 0.07s ===============================================================================


3、执行命令:allure serve ./results_2 去开启一个web server,打开刚才的报告

(pytest_study_virtualenv) D:\PycharmProject\pytest_study>allure serve ./results_2
Generating report to temp directory...
Report successfully generated to C:\Users\WINDOW~1\AppData\Local\Temp\5434101673623352375\allure-report
Starting web server...
2022-08-24 21:54:31.550:INFO::main: Logging initialized @1975ms to org.eclipse.jetty.util.log.StdErrLog
Server started at <http://2.0.0.1:58347/>. Press <Ctrl+C> to exit

4、通过另外一种方式生成报告:allure generate ./results_2,生成本地报告,需要重写就–clean,这个报告的文件包名为allure-report,点进去找到index.html,直接在pycharm中打开

(pytest_study_virtualenv) D:\PycharmProject\pytest_study>allure generate ./results_2
Allure: Target directory D:\PycharmProject\pytest_study\allure-report for the report is already in use, add a '--clean' option to overwrite

(pytest_study_virtualenv) D:\PycharmProject\pytest_study>allure generate ./results_2 --clean
Report successfully generated to allure-report

对生成的本地报告的不使用pycharm的打开方式:开启一个本地服务allure open -h 127.0.0.1 -p 8883 ./allure-report(其他人有allure的环境,可以把allure-report文件包发给对方,对方可使用此方式打开报告)

(pytest_study_virtualenv) D:\PycharmProject\pytest_study>allure open -h 127.0.0.1 -p 8883 ./allure-report
Starting web server...
2022-08-24 22:05:29.347:INFO::main: Logging initialized @188ms to org.eclipse.jetty.util.log.StdErrLog
Server started at <http://activate.navicat.com:8883/>. Press <Ctrl+C> to exit

5、合并报告:allure serve ./results_2 ./results,跟上多个报告的相对路径即可,或者使用allure generate -c -o ./allure-report ./results_2 ./results 生成本地报告,然后用pycharm或者open命令打开

(pytest_study_virtualenv) D:\PycharmProject\pytest_study>allure serve ./results_2 ./results
Generating report to temp directory...
Report successfully generated to C:\Users\WINDOW~1\AppData\Local\Temp\7094238750771544560\allure-report
Starting web server...
2022-08-24 22:14:46.094:INFO::main: Logging initialized @2033ms to org.eclipse.jetty.util.log.StdErrLog
Server started at <http://2.0.0.1:62218/>. Press <Ctrl+C> to exit

pytest-allure库的常见使用(语法糖)

语法糖 参数值 说明
@allure.feature() 模块级别 比如【采购】、【资金】这种大模块:@allure.feature(“采购”)
@allure.story() 功能级别 比如”采购单创建“这种小模块:@allure.story(“采购单创建单元”)
@allure.title() 用例级别 比如"采购单创建-以销定采"这种用例名:@allure.story("“采购单创建-以销定采”")
@allure.description() 用例描述
with allure.step() 操作步骤
@allure.severity() 用例等级 blocker、critical、normal、minor、trivial
"""
=========================
File: test_allure.py
Author: dancing
Time: 2022/8/24
E-mail: 1@1.com
=========================
"""
import pytest
import allure


@allure.feature("采购模块")
@allure.story("创建采购单")
@allure.title("创建以销定采的采购单1,创建成功")
@allure.description("冒烟")
def test_purchase_001():
    with allure.step("步骤一:输入xxx"):
        assert 1 == 1
    with allure.step("步骤二:输入xxx"):
        assert 1 == 2


@allure.feature("采购模块")
@allure.story("创建采购单")
@allure.title("创建以销定采的采购单2,创建成功")
def test_purchase_002():
    assert 1 == 1


@allure.feature("采购模块")
@allure.story("创建采购单")
@allure.title("创建采购直发的采购单,创建成功")
def test_purchase_003():
    assert 1 == 1


@allure.feature("采购模块")
@allure.story("删除采购单")
@allure.title("删除单个采购单,成功")
def test_purchase_004():
    assert 1 == 1


@allure.feature("采购模块")
@allure.story("删除采购单")
@allure.title("批量删除采购单,成功")
def test_purchase_005():
    assert 1 == 1

清除之前的报告:pytest test_allure.py --alluredir ./results --clean-alluredir

(pytest_study_virtualenv) D:\PycharmProject\pytest_study>pytest test_allure.py --alluredir ./results --clean-alluredir
================================================================================== test session starts ===================================================================================
platform win32 -- Python 3.7.4, pytest-7.1.2, pluggy-1.0.0
rootdir: D:\PycharmProject\pytest_study, configfile: pytest.ini
plugins: allure-pytest-2.9.45, encode-1.0.1, forked-1.4.0, ordering-0.6, xdist-2.5.0
collected 5 items                                                                                                                                                                         

test_allure.py ....F                                                                                                                                                                [100%]

======================================================================================== FAILURES ========================================================================================
___________________________________________________________________________________ test_purchase_001 ____________________________________________________________________________________

    @allure.feature("采购模块")
    @allure.story("创建采购单")
    @allure.title("创建以销定采的采购单1,创建成功")
    @allure.description("冒烟")
    def test_purchase_001():
        with allure.step("步骤一:输入xxx"):
            assert 1 == 1
        with allure.step("步骤二:输入xxx"):
>           assert 1 == 2
E           assert 1 == 2

test_allure.py:21: AssertionError
================================================================================ short test summary info =================================================================================
FAILED test_allure.py::test_purchase_001 - assert 1 == 2
============================================================================== 1 failed, 4 passed in 0.04s ===============================================================================

(pytest_study_virtualenv) D:\PycharmProject\pytest_study>allure serve ./results
Generating report to temp directory...
Report successfully generated to C:\Users\WINDOW~1\AppData\Local\Temp\8864096194695011783\allure-report
Starting web server...
2022-08-24 22:50:10.241:INFO::main: Logging initialized @1954ms to org.eclipse.jetty.util.log.StdErrLog
Server started at <http://2.0.0.1:52463/>. Press <Ctrl+C> to exit

十三、Pytest Fixture

官方文档:How to use fixtures — pytest documentation

一、fixture优势:

1、 命名灵活,不局限于setup和teardown

2、 所有fixture都可写在一个conftest.py文件中,供所有测试用例使用

3、 conftest.py配置里可以实现数据共享前置,不需要import 代码自动识别

4、 scop=”module” 可实现多个.py跨文件共享前置,每个.py文件调用一次

5、 scop=”session” 可实现多个.py跨文件使用一个session完成多个用例

6、实现参数化

二、基本用法:

场景:测试用例执行时,有的用例需要登录才能执行,有些用例不需要登录。setup和teardown无法满足。fixture可以实现,默认scope范围function

步骤:导入pytest、在登录的函数上面添加@pytest.fixture()、在要使用的测试方法中传入登录函数的名称,就先登录、不传入的就直接运行测试方法

1、

场景1:全部的用例都需要登录,那好办,直接在setup_class中调用登录方法就好

场景2:假如只是其中一些用例需要登录,有一些不需要登录。那setup就不好用了,然后通常的方式是:在需要登录的测试用例执行前,去调用登录接口。但这样很麻烦。所以fixture提供一个便捷的方式:装饰登录函数,然后在需要的用例中,传入登录函数名,即可

"""
=========================
File: test_fixture.py
Author: dancing
Time: 2022/8/25
E-mail: 1@1.com
=========================
"""
import pytest


@pytest.fixture()
def login():
    print("登录成功")


def test_search():
    print("进行搜索")


def test_cart(login):
    print("进行加购")

执行结果:

(pytest_study_virtualenv) D:\PycharmProject\pytest_study>pytest test_fixture.py -vs
================================================================================== test session starts ===================================================================================
platform win32 -- Python 3.7.4, pytest-7.1.2, pluggy-1.0.0 -- d:\virtualenv\pytest_study_virtualenv\scripts\python.exe
cachedir: .pytest_cache
rootdir: D:\PycharmProject\pytest_study, configfile: pytest.ini
plugins: allure-pytest-2.9.45, encode-1.0.1, forked-1.4.0, ordering-0.6, xdist-2.5.0
collected 2 items                                                                                                                                                                         

test_fixture.py::test_cart 登录成功
进行加购
PASSED
test_fixture.py::test_search 进行搜索
PASSED

=================================================================================== 2 passed in 0.02s ====================================================================================


三、fixture作用域

范围 说明
function 函数级 每个函数/方法都会调用
class 类级别 每个测试类只运行一次
module 模块级 每个py文件调用一次
package 包级 每个python包只调用一次
session 会话级 每次会话只需要运行一次(一次自动化测试,可能运行多个package,即一次会话。session是最大级别)

这个范围,通过scope参数进行设置:

@pytest_fixture(scope=“function”)

"""
=========================
File: test_fixture.py
Author: dancing
Time: 2022/8/25
E-mail: 1@1.com
=========================
"""
import pytest


@pytest.fixture(scope="function")
def login():
    print("登录成功")


class TestMall:

    def test_search(self):
        print("进行搜索")

    def test_cart(self, login):
        print("进行加购")

    def test_order(self, login):
        print("进行下单")

第一次执行:(scope=“class”)

第二次执行:(scope=“function”)

(pytest_study_virtualenv) D:\PycharmProject\pytest_study>pytest test_fixture.py -vs
================================================================================== test session starts ===================================================================================
platform win32 -- Python 3.7.4, pytest-7.1.2, pluggy-1.0.0 -- d:\virtualenv\pytest_study_virtualenv\scripts\python.exe
cachedir: .pytest_cache
rootdir: D:\PycharmProject\pytest_study, configfile: pytest.ini
plugins: allure-pytest-2.9.45, encode-1.0.1, forked-1.4.0, ordering-0.6, xdist-2.5.0
collected 3 items                                                                                                                                                                         

test_fixture.py::TestMall::test_order 登录成功
进行下单
PASSED
test_fixture.py::TestMall::test_cart 进行加购
PASSED
test_fixture.py::TestMall::test_search 进行搜索
PASSED

=================================================================================== 3 passed in 0.02s ====================================================================================

(pytest_study_virtualenv) D:\PycharmProject\pytest_study>pytest test_fixture.py -vs
================================================================================== test session starts ===================================================================================
platform win32 -- Python 3.7.4, pytest-7.1.2, pluggy-1.0.0 -- d:\virtualenv\pytest_study_virtualenv\scripts\python.exe
cachedir: .pytest_cache
rootdir: D:\PycharmProject\pytest_study, configfile: pytest.ini
plugins: allure-pytest-2.9.45, encode-1.0.1, forked-1.4.0, ordering-0.6, xdist-2.5.0
collected 3 items                                                                                                                                                                         

test_fixture.py::TestMall::test_order 登录成功
进行下单
PASSED
test_fixture.py::TestMall::test_cart 登录成功
进行加购
PASSED
test_fixture.py::TestMall::test_search 进行搜索
PASSED

=================================================================================== 3 passed in 0.02s ====================================================================================


四、yield关键字

fixture中,yield关键字实现teardown,同时yield可以当做return返回数据

"""
=========================
File: test_fixture.py
Author: dancing
Time: 2022/8/25
E-mail: 1@1.com
=========================
"""
import pytest


@pytest.fixture(scope="function")
def login():
    print("登录成功")
    token = "aqewfq wfasfefawef"
    yield "yield 同时类似于return,可以返回数据,比如返回这句话", token
    print("退出登录")


def test_search():
    print("进行搜索")


def test_cart(login):
    print("获取登录信息:{}".format(login[0]))
    print("进行加购")


def test_order(login):
    print("获取登录token:{}".format(login[1]))
    print("进行下单")

执行结果:

(pytest_study_virtualenv) D:\PycharmProject\pytest_study>pytest test_fixture.py -vs
================================================================================== test session starts ===================================================================================
platform win32 -- Python 3.7.4, pytest-7.1.2, pluggy-1.0.0 -- d:\virtualenv\pytest_study_virtualenv\scripts\python.exe
cachedir: .pytest_cache
rootdir: D:\PycharmProject\pytest_study, configfile: pytest.ini
plugins: allure-pytest-2.9.45, encode-1.0.1, forked-1.4.0, ordering-0.6, xdist-2.5.0
collected 3 items                                                                                                                                                                         

test_fixture.py::test_order 登录成功
获取登录token:aqewfq wfasfefawef
进行下单
PASSED退出登录

test_fixture.py::test_cart 登录成功
获取登录信息:yield 同时类似于return,可以返回数据,比如返回这句话
进行加购
PASSED退出登录

test_fixture.py::test_search 进行搜索
PASSED

=================================================================================== 3 passed in 0.02s ====================================================================================


五、数据共享

通过使用conftest.py进行数据共享

0、当你在conftest.py中创建一些方法,在执行时,执行文件不需要调用,pytest在运行时首先就会去运行conftest.py,类似于自动初始化的一个功能。

1、conftest.py由自己去创建

2、在不同的目录下,conftest.py有着不同的作用域

3、conftest.py这个名字是固定的,不能修改,否则识别不了

继续用登录的例子对conftest.py的用法进行一个实践:

1、File: conftest.py

"""
=========================
File: conftest.py
Author: dancing
Time: 2022/8/25
E-mail: 1@1.com
=========================
"""
import pytest


@pytest.fixture(scope="function")
def login():
    print("登录成功")
    token = "aqewfq wfasfefawef"
    yield "yield 同时类似于return,可以返回数据,比如返回这句话", token
    print("退出登录")
    

@pytest.fixture(scope="function")
def connect_mysql():
    print("数据库连接成功")
    yield
    print("数据库断开连接")

2、File: test_fixture.py

"""
=========================
File: test_fixture.py
Author: dancing
Time: 2022/8/25
E-mail: 1@1.com
=========================
"""



def test_search():
    print("进行搜索")


def test_cart(login):
    print("获取登录信息:{}".format(login[0]))
    print("进行加购")


def test_order(login):
    print("获取登录token:{}".format(login[1]))
    print("进行下单")

3、执行结果:可以看到test_fixture.py中的一些变量并不存在,但由于同级目录下存在conftest.py,定义了这些变量,所以执行结果是成功的,实现了数据共享

(pytest_study_virtualenv) D:\PycharmProject\pytest_study>pytest test_fixture.py -vs
================================================================================== test session starts ===================================================================================
platform win32 -- Python 3.7.4, pytest-7.1.2, pluggy-1.0.0 -- d:\virtualenv\pytest_study_virtualenv\scripts\python.exe
cachedir: .pytest_cache
rootdir: D:\PycharmProject\pytest_study, configfile: pytest.ini
plugins: allure-pytest-2.9.45, encode-1.0.1, forked-1.4.0, ordering-0.6, xdist-2.5.0
collected 3 items                                                                                                                                                                         

test_fixture.py::test_order 登录成功
获取登录token:aqewfq wfasfefawef
进行下单
PASSED退出登录

test_fixture.py::test_cart 登录成功
获取登录信息:yield 同时类似于return,可以返回数据,比如返回这句话
进行加购
PASSED退出登录

test_fixture.py::test_search 进行搜索
PASSED

=================================================================================== 3 passed in 0.02s ====================================================================================

4、注释掉conftest.py再次运行test_fixture.py:报错E fixture ‘login’ not found

(pytest_study_virtualenv) D:\PycharmProject\pytest_study>pytest test_fixture.py -vs
================================================================================== test session starts ===================================================================================
platform win32 -- Python 3.7.4, pytest-7.1.2, pluggy-1.0.0 -- d:\virtualenv\pytest_study_virtualenv\scripts\python.exe
cachedir: .pytest_cache
rootdir: D:\PycharmProject\pytest_study, configfile: pytest.ini
plugins: allure-pytest-2.9.45, encode-1.0.1, forked-1.4.0, ordering-0.6, xdist-2.5.0
collected 3 items                                                                                                                                                                         

test_fixture.py::test_order ERROR
test_fixture.py::test_cart ERROR
test_fixture.py::test_search 进行搜索
PASSED

========================================================================================= ERRORS =========================================================================================
______________________________________________________________________________ ERROR at setup of test_order ______________________________________________________________________________
file D:\PycharmProject\pytest_study\test_fixture.py, line 21
  def test_order(login):
E       fixture 'login' not found
>       available fixtures: cache, capfd, capfdbinary, caplog, capsys, capsysbinary, doctest_namespace, monkeypatch, pytestconfig, record_property, record_testsuite_property, record_xml_a
ttribute, recwarn, testrun_uid, tmp_path, tmp_path_factory, tmpdir, tmpdir_factory, worker_id
>       use 'pytest --fixtures [testpath]' for help on them.

D:\PycharmProject\pytest_study\test_fixture.py:21
______________________________________________________________________________ ERROR at setup of test_cart _______________________________________________________________________________
file D:\PycharmProject\pytest_study\test_fixture.py, line 16
  def test_cart(login):
E       fixture 'login' not found
>       available fixtures: cache, capfd, capfdbinary, caplog, capsys, capsysbinary, doctest_namespace, monkeypatch, pytestconfig, record_property, record_testsuite_property, record_xml_a
ttribute, recwarn, testrun_uid, tmp_path, tmp_path_factory, tmpdir, tmpdir_factory, worker_id
>       use 'pytest --fixtures [testpath]' for help on them.

D:\PycharmProject\pytest_study\test_fixture.py:16
================================================================================ short test summary info =================================================================================
ERROR test_fixture.py::test_order
ERROR test_fixture.py::test_cart
============================================================================== 1 passed, 2 errors in 0.03s ===============================================================================

六、fixture自动应用

本身使用fixture,需要在测试用例中添加参数:对应的fixture装饰的函数的函数名。但可以通过autouse=True实现自动应用,不再需要添加函数名

1、不再添加login参数

"""
=========================
File: test_fixture.py
Author: dancing
Time: 2022/8/25
E-mail: 1@1.com
=========================
"""
import pytest


@pytest.fixture(scope="function", autouse=True)
def login():
    print("登录成功")
    token = "aqewfq wfasfefawef"
    yield "yield 同时类似于return,可以返回数据,比如返回这句话", token
    print("退出登录")


def test_search():
    print("进行搜索")


def test_cart():
    print("获取登录信息:{}".format(login[0]))
    print("进行加购")


def test_order():
    print("获取登录token:{}".format(login[1]))
    print("进行下单")

2、执行结果:可以看到每一个测试用例,都调用了login。但同时,由于没有login参数,在此种情况下进行调用会出现报错

(pytest_study_virtualenv) D:\PycharmProject\pytest_study>pytest test_fixture.py -vs
================================================================================== test session starts ===================================================================================
platform win32 -- Python 3.7.4, pytest-7.1.2, pluggy-1.0.0 -- d:\virtualenv\pytest_study_virtualenv\scripts\python.exe
cachedir: .pytest_cache
rootdir: D:\PycharmProject\pytest_study, configfile: pytest.ini
plugins: allure-pytest-2.9.45, encode-1.0.1, forked-1.4.0, ordering-0.6, xdist-2.5.0
collected 3 items                                                                                                                                                                         

test_fixture.py::test_order 登录成功
FAILED退出登录

test_fixture.py::test_cart 登录成功
FAILED退出登录

test_fixture.py::test_search 登录成功
进行搜索
PASSED退出登录


======================================================================================== FAILURES ========================================================================================
_______________________________________________________________________________________ test_order _______________________________________________________________________________________

    def test_order():
>       print("获取登录token:{}".format(login[1]))
E       TypeError: 'function' object is not subscriptable

test_fixture.py:30: TypeError
_______________________________________________________________________________________ test_cart ________________________________________________________________________________________

    def test_cart():
>       print("获取登录信息:{}".format(login[0]))
E       TypeError: 'function' object is not subscriptable

test_fixture.py:25: TypeError
================================================================================ short test summary info =================================================================================
FAILED test_fixture.py::test_order - TypeError: 'function' object is not subscriptable
FAILED test_fixture.py::test_cart - TypeError: 'function' object is not subscriptable
============================================================================== 2 failed, 1 passed in 0.04s ===============================================================================

七、fixture实现参数化

1、首先,在@pytest.fixture中通过param参数进行传递参数值:@pytest.fixture(params=[[“admin”, 123456], [“admin”, “admin”]])

2、然后被装饰函数用内置的被@pytest.fixture装饰的request函数进行接收(request是pytest的一个内置fixture函数当他作为参数时,具有request.param属性,用来获取请求的参数)官方文档:https://docs.pytest.org/en/7.1.x/reference/reference.html#request、https://docs.pytest.org/en/7.1.x/example/simple.html#request-example

"""
=========================
File: test_fixture.py
Author: dancing
Time: 2022/8/25
E-mail: 1@1.com
=========================
"""
import pytest


@pytest.fixture(scope="function", autouse=False,
                params=[["admin", 123456], ["admin", "admin"]])
def login(request):
    print("参数为{}".format(request.param))
    return request.param


def test_cart(login):
    print("登录名为:{}".format(login[0]))
    print("密码为:{}".format(login[1]))
(pytest_study_virtualenv) D:\PycharmProject\pytest_study>pytest test_fixture.py -vs
================================================================================== test session starts ===================================================================================
platform win32 -- Python 3.7.4, pytest-7.1.2, pluggy-1.0.0 -- d:\virtualenv\pytest_study_virtualenv\scripts\python.exe
cachedir: .pytest_cache
rootdir: D:\PycharmProject\pytest_study, configfile: pytest.ini
plugins: allure-pytest-2.9.45, encode-1.0.1, forked-1.4.0, ordering-0.6, xdist-2.5.0
collected 2 items                                                                                                                                                                         

test_fixture.py::test_cart[login1] 参数为['admin', 'admin']
登录名为:admin
密码为:admin
PASSED
test_fixture.py::test_cart[login0] 参数为['admin', 123456]
登录名为:admin
密码为:123456
PASSED

=================================================================================== 2 passed in 0.02s ====================================================================================

十四、Pytest ini配置文件

1、文件名固定,必须叫pytest.ini

2、文件内第一行必须用[pytest]声明这是pytest.ini文件

3、文件放置于项目根目录

4、此文件作用:

  • 是修改一些默认的配置,比如测试用例默认为test_开头,但可以通过这个ini文件进行修改
  • 配置日志
  • 添加标签
  • 指定执行目录
  • 排除搜索目录

5、不能使用中文

  1. 修改命名规则:

    默认的是这样的这样的↓

    [pytest]
    python_files =  test_* 
    python_functions = test_*
    python_classes = Test*
    

    进行修改:

    [pytest]
    python_files = zg_test_*.py test_*.py
    python_functions = zg_test_* test_*
    python_classes = Zg_test* Test*
    

    代码:

    """
    =========================
    File: zhaogang_test_ini.py
    Author: dancing
    Time: 2022/8/25
    E-mail: 1@1.com
    =========================
    """
    
    
    class Zg_test_ini:
    
        def zg_test_demo_001(self):
            print("测试修改用例名称")
    
    
    def test_001():
        print("aaaa")
    

    效果:

    (pytest_study_virtualenv) D:\PycharmProject\pytest_study>pytest zg_test_ini.py
    ================================================================================== test session starts ===================================================================================
    platform win32 -- Python 3.7.4, pytest-7.1.2, pluggy-1.0.0 -- d:\virtualenv\pytest_study_virtualenv\scripts\python.exe
    cachedir: .pytest_cache
    rootdir: D:\PycharmProject\pytest_study, configfile: pytest.ini
    plugins: allure-pytest-2.9.45, encode-1.0.1, forked-1.4.0, ordering-0.6, xdist-2.5.0
    collected 2 items                                                                                                                                                                         
    
    zg_test_ini.py::test_001 aaaa
    PASSED
    zg_test_ini.py::Zg_test_ini::zg_test_demo_001 测试修改用例名称
    PASSED
    
    =================================================================================== 2 passed in 0.02s ====================================================================================
    
  2. 修改命令行参数

    没有配置时,执行命令:pytest test_ini.py

    (pytest_study_virtualenv) D:\PycharmProject\pytest_study>pytest test_ini.py
    ================================================================================== test session starts ===================================================================================
    platform win32 -- Python 3.7.4, pytest-7.1.2, pluggy-1.0.0
    rootdir: D:\PycharmProject\pytest_study, configfile: pytest.ini
    plugins: allure-pytest-2.9.45, encode-1.0.1, forked-1.4.0, ordering-0.6, xdist-2.5.0
    collected 1 item                                                                                                                                                                          
    
    test_ini.py .                                                                                                                                                                       [100%]
    
    =================================================================================== 1 passed in 0.01s ====================================================================================
    

    没有配置,但加上-vs参数:pytest -vs test_ini.py

    (pytest_study_virtualenv) D:\PycharmProject\pytest_study>pytest -vs test_ini.py
    ================================================================================== test session starts ===================================================================================
    platform win32 -- Python 3.7.4, pytest-7.1.2, pluggy-1.0.0 -- d:\virtualenv\pytest_study_virtualenv\scripts\python.exe
    cachedir: .pytest_cache
    rootdir: D:\PycharmProject\pytest_study, configfile: pytest.ini
    plugins: allure-pytest-2.9.45, encode-1.0.1, forked-1.4.0, ordering-0.6, xdist-2.5.0
    collected 1 item                                                                                                                                                                          
    
    test_ini.py::test_001 测试成功
    PASSED
    
    =================================================================================== 1 passed in 0.01s ====================================================================================
    

    让命令行默认加上-vs参数:

    [pytest]
    
    addopts = -vs
    
    (pytest_study_virtualenv) D:\PycharmProject\pytest_study>pytest test_ini.py
    ================================================================================== test session starts ===================================================================================
    platform win32 -- Python 3.7.4, pytest-7.1.2, pluggy-1.0.0 -- d:\virtualenv\pytest_study_virtualenv\scripts\python.exe
    cachedir: .pytest_cache
    rootdir: D:\PycharmProject\pytest_study, configfile: pytest.ini
    plugins: allure-pytest-2.9.45, encode-1.0.1, forked-1.4.0, ordering-0.6, xdist-2.5.0
    collected 1 item                                                                                                                                                                          
    
    test_ini.py::test_001 测试成功
    PASSED
    
    =================================================================================== 1 passed in 0.01s ====================================================================================
    

    自动加上allure的报告:

    [pytest]
    
    addopts = -vs --alluredir ./results_002 --clean-alluredir
    ;addopts = -vs
    
    
  3. 指定忽略一些执行路径:(用于比如日志或者报告过多时,减少在这些文件上花费过多的检索用例的时间)pytest --help :[pytest] :norecursedirs (args): directory patterns to avoid for recursion

    [pytest]
    
    norecursedirs = results_002
    
  4. 指定执行的目录

    [pytest]
    
    testpaths = testcase
    
  5. 配置日志

    [pytest]
    
    log_cli=true
    log_level=info
    log_format = %(asctime)s %(levelname)s %(message)s (%(filename)s:%(lineno)s)
    log_date_format = %Y-%m-%d %H:%M:%S
    log_file = ./log/test.log
    log_file_level = info
    log_file_format = %(asctime)s %(levelname)s %(message)s (%(filename)s:%(lineno)s)
    log_file_date_format = %Y-%m-%d %H:%M:%S
    addopts = --capture=no
    

    效果:第10行,对应日志文件也同时有效果

    (pytest_study_virtualenv) D:\PycharmProject\pytest_study>pytest test_log.py
    ================================================================================== test session starts ===================================================================================
    platform win32 -- Python 3.7.4, pytest-7.1.2, pluggy-1.0.0
    rootdir: D:\PycharmProject\pytest_study, configfile: pytest.ini
    plugins: allure-pytest-2.9.45, encode-1.0.1, forked-1.4.0, ordering-0.6, xdist-2.5.0
    collected 1 item                                                                                                                                                                          
    
    test_log.py::test_log_001
    ------------------------------------------------------------------------------------- live log call --------------------------------------------------------------------------------------
    2022-08-25 22:58:34 INFO 测试日志打印 (test_log.py:13)
    PASSED
    
    =================================================================================== 1 passed in 0.02s ====================================================================================
    

    【注】:解决pytest.ini 配置日志文件每次都覆盖上一次的日志

    通过修改源码的写入方式可以保存所有执行日志,改成 a 模式
    修改文件位置 …/Lib/site-packages/_pytest/logging.py
    将 552行 的 self.log_file_handler = _FileHandler(log_file, mode=“w”, encoding=“UTF-8”)
    修改为:self.log_file_handler = _FileHandler(log_file, mode=“a”, encoding=“UTF-8”)

  6. mark标记

    @pytest.mark.自定义标签

    可以通过ini文件的markers去注册,注册了pytest即可以识别到

    [pytest]
    markers =
        purchase
        sale
    

十五、Pytest 插件

分类:

  • 外部插件:pip install 插件名(第三方库)
  • 本地插件:pytest自动模块发现机制(自己编写,放conftest.py管理)
  • 内置插件:代码内部的_pytest 目录加载(hook函数)

常用外部插件(pip install 插件名):

  • pytest-ordering:控制用例执行顺序

  • pytest-xdist:分布式并发执行用例(进程)

    分别用4cpu、16cpu、自动运行,时间对比:

    """
    =========================
    File: test_xdist.py
    Author: dancing
    Time: 2022/8/25
    E-mail: 1@1.com
    =========================
    """
    import logging
    import time
    import pytest
    
    
    data = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]
    
    
    @pytest.mark.parametrize("times", data)
    def test_001(times):
        logging.info("这是第{}条用例".format(times))
        time.sleep(1)
    
    =================================================================================== 16 passed in 4.59s ===================================================================================
    
    (pytest_study_virtualenv) D:\PycharmProject\pytest_study>pytest -n 4 test_xdist.py
    
    
    =================================================================================== 16 passed in 2.46s ===================================================================================
    
    (pytest_study_virtualenv) D:\PycharmProject\pytest_study>pytest -n 16 test_xdist.py
    
    
    =================================================================================== 16 passed in 2.32s ===================================================================================
    
    (pytest_study_virtualenv) D:\PycharmProject\pytest_study>pytest -n auto test_xdist.py
    
    
  • pytest-html:html格式测试报告

  • pytest-rerunfailures:失败重跑

  • pytest-assume:多重校验

  • pytest-dependency:用例依赖

  • pytest-random-order:用例随机执行

hook函数

  • hook函数就是在一定条件下才会执行的函数,将自己实现的函数挂载到挂载点上
  • 在对应的系统消息触发时被系统调用
  • 自动触发
  • pytest有很多钩子函数
  • 放在conftest.py下去实现

pytest执行用例的顺序:

  • 收集测试用例

  • 执行测试用例setup

  • 调用执行测试

  • 执行测试teardown

  • 获取测试结果

  • hook官方源码:_pytest.hookspec — pytest documentation

  • hook源码路径:D:\virtualenv\pytest_study_virtualenv\Lib\site-packages_pytest\hookspec.py

  • hook执行顺序:

    root
    └── pytest_cmdline_main
    ├── pytest_plugin_registered
    ├── pytest_configure
    │ └── pytest_plugin_registered
    ├── pytest_sessionstart
    │ ├── pytest_plugin_registered
    │ └── pytest_report_header
    ├── pytest_collection
    │ ├── pytest_collectstart
    │ ├── pytest_make_collect_report
    │ │ ├── pytest_collect_file
    │ │ │ └── pytest_pycollect_makemodule
    │ │ └── pytest_pycollect_makeitem
    │ │ └── pytest_generate_tests
    │ │ └── pytest_make_parametrize_id
    │ ├── pytest_collectreport
    │ ├── pytest_itemcollected
    │ ├── pytest_collection_modifyitems
    │ └── pytest_collection_finish
    │ └── pytest_report_collectionfinish
    ├── pytest_runtestloop
    │ └── pytest_runtest_protocol
    │ ├── pytest_runtest_logstart
    │ ├── pytest_runtest_setup
    │ │ └── pytest_fixture_setup
    │ ├── pytest_runtest_makereport
    │ ├── pytest_runtest_logreport
    │ │ └── pytest_report_teststatus
    │ ├── pytest_runtest_call
    │ │ └── pytest_pyfunc_call
    │ ├── pytest_runtest_teardown
    │ │ └── pytest_fixture_post_finalizer
    │ └── pytest_runtest_logfinish
    ├── pytest_sessionfinish
    │ └── pytest_terminal_summary
    └── pytest_unconfigure
    

    decode的作用是将二进制数据解码成unicode编码,如str1.decode(‘utf-8’),表示将utf-8的编码字符串解码成unicode编码。简单的来说:decode就是把二进制数据(bytes)转化成人看的懂得英文或者汉字(decode用的比较多)

    encode的作用是将unicode编码的字符串编码成二进制数据,如str2.encode(‘utf-8’),表示将unicode编码的字符串编码成utf-8。

插件编写:

示例:pytest_collection_modifyitems收集测试用例订制化

定制化问题解决:解决编码问题(中文用例名称生效)

"""
=========================
File: conftest.py
Author: dancing
Time: 2022/8/28
E-mail: 1@1.com
=========================
"""
from typing import List

import pytest


def pytest_collection_modifyitems(
    session: "Session", config: "Config", items: List["Item"]
) -> None:
    # print("test pytest_collection_modifyitems")
    for item in items:
        item._nodeid = item.nodeid.encode('utf-8').decode('unicode-escape')

插件编写:添加命令行参数

1、重新hook函数:pytest_addoption

2、定义一个fixture:cmd_option,通过request参数去拿到命令行对应的环境返回对应环境的数据

"""
=========================
File: conftest.py
Author: dancing
Time: 2022/8/25
E-mail: 1@1.com
=========================
"""
from typing import List

import pytest
import yaml




def pytest_addoption(parser: "Parser",
                     pluginmanager: "PytestPluginManager") -> None:
    group_zhaogang = parser.getgroup("zhaogang")
    group_zhaogang.addoption("--env", default='test', help='设置系统环境')


@pytest.fixture(scope="session")
def cmd_option(request):
    zhaogang_env = request.config.getoption("--env", default='test')
    if zhaogang_env == 'test':
        data_path = './data/test/data.yaml'
    elif zhaogang_env == 'uat':
        data_path = './data/uat/data.yaml'

    with open(data_path) as f:
        data = yaml.safe_load(f)
    return zhaogang_env, data

yaml文件:

env:
  ip: https://111.com
  port: 80

3、测试

"""
=========================
File: test_cmd_option.py
Author: dancing
Time: 2022/8/28
E-mail: 1@1.com
=========================
"""


def test_cmd_option(cmd_option):
    print(cmd_option)
(pytest_study_virtualenv) D:\PycharmProject\pytest_study>pytest --env uat test_cmd_option.py -vs
================================================================================== test session starts ===================================================================================
platform win32 -- Python 3.7.4, pytest-7.1.2, pluggy-1.0.0 -- d:\virtualenv\pytest_study_virtualenv\scripts\python.exe
cachedir: .pytest_cache
rootdir: D:\PycharmProject\pytest_study, configfile: pytest.ini
plugins: allure-pytest-2.9.45, forked-1.4.0, ordering-0.6, xdist-2.5.0
collecting ... test pytest_collection_modifyitems
collected 1 item                                                                                                                                                                          

test_cmd_option.py::test_cmd_option ('uat', {'env': {'ip': 'https://111.com', 'port': 80}})
PASSED

=================================================================================== 1 passed in 0.02s ====================================================================================

打包发布插件:(代码仓库拉代码 or 打包发布到pypi然后通过pip安装)

打包结构:

"""
=========================
File: setup.py.py
Author: dancing
Time: 2022/8/28
E-mail: 1@1.com
=========================
"""
import setuptools

with open("README.md", "r") as fh:
    long_description = fh.read()

setuptools.setup(
    name="dixkss",
    version="0.0.3",
    author="11",
    author_email="1@1.com",
    description="decode unicode-eacape",
    long_description='no long_description',
    long_description_content_type="text/markdown",
    url="https://github.com/pypa/sampleproject",
    # project_urls={
    # 	"Bug Tracker": "https://github.com/pypa/sampleproject/issues"
    # },
    classifiers=[
        "Framework :: Pytest",
        "Programming Language :: Python",
        "Topic :: Software Development",
        "Programming Language :: Python :: 3.7",
    ],
    license='proprietary',
    packages = setuptools.find_packages(),
    keywords=[
        'pytest', 'py.test', 'dixksstion',
    ],
    install_requires=[
        'pytest'
    ],
    entry_points={
        'pytest11': [
            'dixksstion = dixksstion.main'
        ]
    },
    zip_safe=False
)

打包命令:

依赖包安装:

pip install setuptools :python包管理工具,负责安装和发布(尤其是安装拥有依赖关系的包)

pip install wheel:生成*.whl 格式的安装包

打包命令:python setup.py sdist bdist_wheel

发布命令:

1、Now that you are registered, you can use twine to upload the distribution packages. You’ll need to install Twine:安装twine

py -m pip install --upgrade twine

2、Once installed, run Twine to upload all of the archives under dist:

py -m twine upload --repository testpypi dist/*

上面这个命令不大行,换成:twine upload dist/*

c盘user/win10目录下,准备好.pypirc文件,还要带上test的部分

[distutils]
index-servers=pypi
				testpypi

[pypi]
repository = https://upload.pypi.org/legacy/
username = 用户名
password = 密码

[testpypi]
repository = https://test.pypi.org/legacy/
username = 用户名
password = 密码

发布完成:

(pytest_hook_bug) D:\PycharmProject\pytest_hook_bug_linshi\dixkss\dixkss>twine upload dist/*
Uploading distributions to https://upload.pypi.org/legacy/
Uploading dixkss-0.0.3-py3-none-any.whl
100% ---------------------------------------- 6.1/6.1 kB • 00:00 • ?
Uploading dixkss-0.0.3.tar.gz
100% ---------------------------------------- 4.8/4.8 kB • 00:00 • ?

View at:
https://pypi.org/project/dixkss/0.0.3/

安装:pip install dixkss

忘记了,可还行

哈哈哈哈哈哈,就好比先当xfailed先标记下

关闭