pytest测试框架

一、安装

pip install pytest

验证安装是否成功

pytest --version

二、使用

1、基本要求:

1、所有的单测文件名都需要满足test_*.py格式或*_test.py格式。

2、在单测文件中,测试类以Test开头,并且不能带有 init 方法(注意:定义class时,需要以T开头,不然pytest是不会去运行该class的)
3、在单测类中,可以包含一个或多个test_开头的函数。

在执行pytest命令时,会自动从当前目录及子目录中寻找符合上述约束的测试函数来执行。

2、使用方法:

1、文件结构框架

- Test_App
- - test_abc.py
- - conftest.py  # 配置文件,可不写
- - pytest.ini  # 配置文件,可不写

2、主运行函数

import  pytest

def test_01():
    print("this is {} testcase".format(__name__))

if __name__=='__main__':
    pytest.main()

3、类命名规则:

class Test_ABC:

3、断言的五种方式:

1、assert xx:判断 xx 为真

2、assert not xx:判断 xx 不为真

3、assert a in b:判断 b 包含 a

4、assert a == b:判断 a 等于 b

5、assert a !=b:判断 a 不等于 b

4、测试用例的执行参数:

1、参数以列表的形式给出

例如:

参数

描述案例
-v 
 
输出调试信息。如:打印信息

pytest.main

([‘-v’,‘testcase/test_one.py’,‘testcase/test_two.py’])

-s 
 
输出更详细的信息,如:文件名、用例名    

pytest.main

([‘-vs’,‘testcase/test_one.py’,‘testcase/test_two.py’])

-n    
 
多线程或分布式运行测试用例    
-x    
 
只要有一个用例执行失败,就停止执行测试    pytest.main([‘-vsx’,‘testcase/test_one.py’])
– maxfail    
 
出现N个测试用例失败,就停止测试   pytest.main([‘-vs’,‘-x=2’,‘testcase/test_one.py’]
–html=report.html       
 
生成测试报告 

pytest.main

([‘-vs’,‘–html=./report.html’,‘testcase/test_one.py’])

-m      
 
通过标记表达式执行  
-k    根据测试用例的部分字符串指定测试用例,可以使用and,or

--count=n执行用例n次

pytest.main

([‘-vs’,‘--count = 2’,‘testcase/test_two.py’])

5、Pytest Exit Code 含义清单

Exit code 0 所有用例执行完毕,全部通过
Exit code 1 所有用例执行完毕,存在Failed的测试用例
Exit code 2 用户中断了测试的执行
Exit code 3 测试执行过程发生了内部错误
Exit code 4 pytest 命令行使用错误
Exit code 5 未采集到可用测试用例文件

6、控制条件

1、在第n个用例失败后,结束测试执行

pytest -x              # 第01次失败,就停止测试
pytest --maxfail=2     # 出现2个失败就终止测试

2、执行指定文件路径/文件

# 执行指定文件路径
pytest ./testcase/
# 或者
r'Testcases'


# 执行指定文件
pytest ./testcase/test_one.py
# 或者
r'Testcases/test_one.py'

3、通过关键字过滤执行

运行包含特定关键字的用例

pytest -k "关键字"

排除包含特定关键字的测试

pytest -k "not 关键字"

组合执行多个关键字的用例(or / and)

pytest -k "关键字1 or 关键字2"


pytest -k "关键字1 and 关键字2"

利用表达式

# 运行包含关键字1并且不包含关键字2的
pytest -k "关键字1 and not 关键字2"

匹配测试类和方法

# 运行类名
pytest -k "类名"

测试失败重试

用例执行失败后,会立即开始重试此用例NUM次,再执行下一条用例

# 需要先安装pytest-rerunfailures
# pip install -U pytest-rerunfailures
pytest test_se.py --reruns NUM
# NUM 表示失败后的重试次数

增加失败之后重试延时(reruns_delay=2  延时2秒)

pytest.main( [‘-vs’,‘–reruns=5’,‘–reruns_delay=10’,‘./testcase/test_debug.py’,‘–report=_report.html’])

4、通过修饰跳过执行

@pytest.mark.skip可以加在函数上,类上,类方法上

@pytest.mark.skip(reason="不执行该用例!!因为没写好!!")

5、pytest.skip()函数

在测试用例执行期间强制跳过不再执行剩余内容

def test_function():
    n = 1
    while True:
        print(f"这是我第{n}条用例")
        n += 1
        if n == 5:
            pytest.skip("我跑五次了不跑了")

7、生成测试报告:

安装pytest-html插件(可生成xml和html的报告)

pip install pytest-html

两种应用方式:

(pytest --html=用户路径/report.html)

1、在pytest.ini中使用

addopts = -s --html=./report.html
# 或者
addopts = -s --html=./report.xml

2、在main.py主函数

pytest.main(["-v", "-s",r'Testcases/test_log.py','--log-cli-level=INFO','--html=report.html','--self-contained-html', f'--alluredir=./{allure_result}'])

node id 指定测试用例:

nodeid由模块文件名、分隔符、类名、方法名、参数构成

pytest test_mod.py::TestClass::test_method

三、高阶用法:

Fixture:定义共享的fixture,供测试用例使用。
钩子函数:实现pytest的钩子函数,以便在测试运行的不同阶段执行特定代码。
插件:可以在 conftest.py 中定义插件,使其自动应用于所有测试。

1、详解conftest.py文件

存放fixture的配置文件(类似于初始化)

不需要执行import 就能自动找到fixture

在多个测试文件中,使用同一个fixture

conftest.py文件名是固定的,不可以修改(放在测试目录下)

conftest.py与运行用例在同一个包下,并且该包中有__init__.py文件

@pytest.fixture()装饰器来装饰一个函数,那么被装饰的这个函数就是一个fixture

 Fixture的参数如下:

@pytest.fixture(scope = "function",params=None,autouse=False,ids=None,name=None)

fixture的作用范围(scope)共有4种:function,class,module,session(默认function)

范围依次递增的:即function < class < module < session

1、当scope=“function”时,只要有测试函数显示调用fixture时,都会执行一次fixture。但是如果测试函数没有调用fixture,那么就不会执行fixture

import pytest
 
@pytest.fixture()
def login():
    print("Login")
    return "hello world"
 
class TestDemo:
    #调用login,则会调用一次fixture
    def test_case1(self,login):
        print("case1")
    #不调用login,则不会调用fixture
    def test_case2(self):
        print("case2")
    #调用login,则第二次调用fixture,加上case1,一共调用了两次
    def test_case3(self,login):
        print("case3")
        assert login == "hello world"

2、当scope=“class”时,有两种场景:

1.测试类下所有的测试方法都调用了fixture,那么fixture只执行一次,时机为测试类中所有测试方执行前。
2.测试类下只有部分测试方法调用了fixture,那么fixture只执行一次,时机为在测试类中第一个调用fixture的测试方法前执行

例1:

import pytest
 
@pytest.fixture(scope="class")
def login():
    print("Scope='class'")
    return "hello world"
 
class TestDemo:
    def test_case1(self,login):
        print("case1")
        assert login == "hello world"
    def test_case2(self,login):
        print("case2")
    def test_case3(self,login):
        print("case3")
        assert login != "hello world"

例2:

import pytest
 
@pytest.fixture(scope="class")
def login():
    print("Scope='class'")
    return "hello world"
 
class TestDemo:
    def test_case1(self):
        print("case1")

    def test_case2(self,login):
        print("case2")

    def test_case3(self,login):
        print("case3")
        assert login == "hello world"

3、当scope=“module”时,则范围会对当前整个.py文件生效

会在第一个调用fixture的测试方法前调用一次然后生效,后面即使再有 其他测试方法调用fixture,也不会再调用

import pytest
 
@pytest.fixture(scope="module")
def login():
    print("Scope='module'")
    return "hello world"
 
def test_case(login):
    print("test_case")
 
class TestDemo:
    def test_case1(self,login):
        print("case1")
    def test_case2(self,login):
        print("case2")
    def test_case3(self,login):
        print("case3")
        assert login == "hello world"

4、当scope=“session”时,则范围会对所有的.py文件生效

conftest.py文件范围应用在这里

多个测试文件只调用一次

例:

# conftest.py

import pytest
 
@pytest.fixture(scope="session")
def login():
    print("Scope='session'")
    return "hello world"
# test_1.py

import pytest
 
def test_case1(login):
    print("case1")
    assert login == "hello world"
 
def test_case2(login):
    print("case2")
 
def test_case3(login):
    print("case3")
# test_2.py

import pytest
 
 
def test_case(login):
    print("test_case")
 
class TestDemo:
    def test_case1(self,login):
        print("case1")
    def test_case2(self,login):
        print("case2")
    def test_case3(self,login):
        print("case3")
        assert login == "hello world"

 2、Fixture使用方式

(fixture修饰器来标记固定的函数,在其他函数,模块,类或整个工程调用它时会被激活并优先执行,通常会被用于完成预置处理和重复操作。)

# 方法
fixture(scope="function", params=None, autouse=False, ids=None, name=None)

# 参数说明
 scope:被标记方法的作用域
 "function"(default):作用于每个测试方法,每个test都运行一次
 "class":作用于整个类,每个class的所有test只运行一次
 "module":作用于整个模块,每个module的所有test只运行一次
 "session":作用于整个session(慎用),每个session只运行一次
 params:(list类型)提供参数数据,供调用标记方法的函数使用
 autouse:是否自动运行,默认为False不运行,设置为True自动运行

params参数示例:

import pytest
@pytest.fixture(params=[1, 2, 3])
def need_data(request): # 传入参数request 系统封装参数
    return request.param # 取列表中单个值,默认的取值方式
class Test_ABC:
 
    def test_a(self,need_data):
        print("------->test_a")
        assert need_data != 3 # 断言need_data不等于3
 
if __name__ == '__main__':
    pytest.main("-s  test_abc.py")
 
 执行结果:
      # 可以发现结果运行了三次
      test_abc.py 
      1
      ------->test_a
      .
      2
      ------->test_a
      .
      3
      ------->test_a
      F

1.作为参数传入

import pytest
 
@pytest.fixture()
def login():
    print("登录")
    return "Account"
 
@pytest.fixture()
def logout():
    print("退出")
    return "Exit"
 
class TestDemo:
    def test_case1(self,login):
        print("我传入了Login Fixture")
        assert login == "Account"
 
    def test_case2(self):
        print("我没传入任何 Fixture")
 
    def test_case3(self,login,logout):
        print("我传入了两个Fixture")
        assert login == "Account"
        assert logout == "Exit"
 
 
if __name__ == '__main__':
    pytest.main(['-s','test_2.py'])

2.fixture相互调用(也相当于作为参数传入)

多层fixture调用时,先执行最后一层fixture,然后再依次向前执行。不会自动return 上一层函数返回的值。

import pytest
 
@pytest.fixture()
def account():
    print("Account")
    return "hello world"
 
#调用上面的account fixture
@pytest.fixture()
def login(account):
    print("Login")
 
class TestDemo:
    def test_case1(self,login):
        print("调用了login,返回值为{}".format(login))
    def test_case2(self,account):
        print("调用了account,返回值为{}".format(account))

3.作为conftest.py文件传入 -- (在多个测试文件中,使用同一个fixture)

例1:

#conftest.py
import pytest
@pytest.fixture()
def paas_token():
    print("开始加载")
    print("加载完成")
# test_db.py
def test_query(paas_token):
    print("--> finish <--")

例2:

@pytest.fixture()
def fixtureFunc():
    return "Hello world"
class TestDemo:
    def test_case1(self,fixtureFunc):
        print(fixtureFunc)
        assert fixtureFunc == "Hello world"

3、pytest.ini文件

pytest的配置文件通常放在测试目录下,名称为pytest.ini,命令行运行时会使用该配置文件中的配置

#配置pytest命令行运行参数
   [pytest]
    addopts = -s ... # 空格分隔,可添加多个命令行参数 -所有参数均为插件包的参数配置测试搜索的路径
    testpaths = ./scripts  # 当前目录下的scripts文件夹 -可自定义
#配置测试搜索的文件名称
    python_files = test*.py 
#当前目录下的scripts文件夹下,以test开头,以.py结尾的所有文件 -可自定义
配置测试搜索的测试类名
    python_classes = Test_*  
 
   #当前目录下的scripts文件夹下,以test开头,以.py结尾的所有文件中,以Test开头的类 -可自定义
配置测试搜索的测试函数名
  
    python_functions = test_*
 
#当前目录下的scripts文件夹下,以test开头,以.py结尾的所有文件中,以Test开头的类内,以test_开头的方法 -可自定义
 

例:

    [pytest]
# 命令行参数
    addopts = -s
# 搜索文件名
    python_files = test_*.py
# 搜索的类名
    python_classes = Test_*
# 搜索的函数名
    python_functions = test_*

4、内置函数(setup和teardown函数)

1.setup和teardown主要分为:模块级,类级,功能级,函数级。
2.存在于测试类内部

例1:

运行于测试方法的始末,即:运行一次测试函数会运行一次setup和teardown

# test_abc.py

import pytest
class Test_ABC:
  # 函数级开始
  def setup(self):
      print("------->setup_method")
  # 函数级结束
  def teardown(self):
      print("------->teardown_method")
  def test_a(self):
      print("------->test_a")
      assert 1
  def test_b(self):
      print("------->test_b")
if __name__ == '__main__':
    pytest.main("-s  test_abc.py")

例2:

运行于测试类的始末,即:在一个测试内只运行一次setup_class和teardown_class,不关心测试类内有多少个测试函数。

import pytest

class Test_ABC:
    # 测试类级开始
    def setup_class(self):
        print("------->setup_class")
    # 测试类级结束
    def teardown_class(self):
        print("------->teardown_class")
    def test_a(self):
        print("------->test_a")
        assert 1
    def test_b(self):
        print("------->test_b")
if __name__ == '__main__':
    pytest.main("-s  test_abc.py")

5、调整用例执行顺序

安装

pip install pytest-ordering

@pytest.mark.run(order=n)  -- 第n个执行

@pytest.mark.last  --  最后一个执行

import pytest
 
@pytest.fixture(scope="class")
def login():
    print("Scope='class'")
    return "hello world"
 
class TestDemo:
    # 修饰最后一个执行
    @pytest.mark.last
    def test_case1(self):
        print("case1")

    # 修饰第n个执行
    @pytest.mark.run(order=n)
    def test_case2(self,login):
        print("case2")

    def test_case3(self,login):
        print("case3")
        assert login == "hello world"

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值