【接口自动化测试】---自动化框架pytest
目录
1、用例运行规则
2、pytest命令参数
3、pytest配置文件
4、前后置
5、断言
6、参数化---对函数的参数(重要)
7、fixture
7.1、基本用法
7.2、fixture嵌套:
7.3、请求多个fixture:
7.4、yield fixture
7.5、带参数的fixture
安装:
pip install pytest==8.3.2
1、用例运行规则
文件名、类名、方法名命名时必须遵循规则,pytest才能自动识别到测试用例,不需要再手动编写main函数调用测试用例。
写一个类:
class Test03():def test03_01(self):print("test03_01")
Test类中为什么不能有__init__方法?
1、pytest采用自动发现机制收集测试用例
2、它会自动实例化测试类并调用test测试方法作为测试用例。
如果写了__init__那么在pytest实例化类的时候,__init__方法就会被调用,这就会掩盖测试类的实际测试用例
那么,我们在初始化的时候应该怎么做呢?
class test02:#python默认构造方法---类的初始化def __init__(self):print("__init__")#类中的普通方法def init(self)print("init")
2、pytest命令参数
pytest -sv .\tests\test_aaa.py::Testaaa::testa_03:指定运行tests目录下的test_aaa.py文件中的Testaaa类中的teata_03方法
3、pytest配置文件
若不配置,则走默认规则。加了配置之后,就可以使用pytest执行不符合规则的文件名、类名、方法名了。
4、前后置
在测试类中,不能有init方法。那我们有初始化的需求该怎么办呢?
pytest框架提供了三种方法做前后置操作
1、setup_method 和 teardown_method:这两个方法用于类中的每个测试方法的前后置操作(setup_method 法一 teardown_method、setup_method 法二 teardown_method)2、setup_class和teardown_class:两个方法用于整个测试类的前后置操作
(setup_clas 法一 teardown_class、 setup_clas 法二 teardown_class)
3、fixture:这是pytest推荐的方式来实现测试用例的前后置操作。这个方法更加灵活,后面专门进行讲解
5、断言
断言时调试的辅助工具,检查代码状态是否符合预期,如果断言失败,即条件为假,就会抛出AssertionError异常。
对返回值内容进行测试。
assert 条件,错误信息
条件:必须是bool值
错误信息:当条件为假时显示,可选。
#断⾔接⼝返回值重要字段
def test2():
url = "http://jsonplaceholder.typicode.com/comments?postId=1"
r = requests.get(url=url)
print(r.json())
assert r.json()[1]['id'] == 1 #下标从0开始
#断⾔接⼝html返回值
def test3():
url = "http://jsonplaceholder.typicode.com/"
r = requests.get(url=url) #r是返回值
assert "Use your own data" in r.text #前面的字符串是不是包含在r.text中
#html返回值是text格式的
6、参数化---对函数的参数(重要)
对测试函数的参数进行参数化,使用pytest内置的pytest.mark.parametrize装饰器
对单个参数的参数化:可以使用不同的数据类型
eg1:在用例上使用参数化:多个参数的参数化
import pytest@pytest.mark.parametrize("test_input,expected",[("3+5",8),("2+4",6),("6*9",54)])def test_eval(test_input,expected):assert eval(test_input) == expected
#通过eval进行计算
给了三组参数,函数就会执行三次
eg2:在类上使用参数化:在类上使用就可以使用参数集调用多个函数
用例可能用到同样的参数,就要对多组用例都使用参数化,那么把参数化定义在类上就是更好的方式。下面的每个用例都会跑两遍。
import pytest
@pytest.mark.parametrize("n,expected", [(1, 2), (3, 4)])class TestClass:
def test_simple_case(self, n, expected):
assert n + 1 == expecte
def test_weird_simple_case(self, n, expected):
assert (n * 1) + 1 == expected
将参数化的结果保存在pytestmark中 表明是一个全局变量,不写pytestmark的时候就要在每个类前面写@pytest.mark.parametrize(.......)
#将参数化的结果保存在pytestmark中 表明是一个全局变量
pytestmark = pytest.mark.parametrize("data",(1,2))
class Test_A:def test_a01(self,data):print(data)def test_a02(self,data):print("data")
class Test_B:def test_b01(self,data):print(data)def test_b02(self,data):print("data")
注意我们在使用pytest的时候,import pytest会在语法层面出现报错,但是实际包是可以找到的,也意味着可以直接使用
自定义参数化数据源,而不是写死的。
#从函数的返回值来获取参数
def data_provider():#....return ["a","b","c"]
@pytest.mark.parametrize("data",data_provider())
def test_data(data):print(data)
7、fixture
7.1、基本用法
可以进行前后置操作,也可以进行参数化。⽤于提供测试函数所需的资源或上下⽂
import pytest@pytest.fixture
def fixture_01():print("第⼀个fixture标记的⽅法")def test_01(fixture_01):print("第⼀个测试⽤例")
测试脚本中存在很多重复的代码、公共的数据对象,使用fixture最为合适。
用例之前必须要先进行登录:
import pytest@pytest.fixture
def login():print("---执⾏登陆操作-----")def test_list(login):print("---访问列表⻚")
def test_detail(login):print("---访问详情⻚")
7.2、fixture嵌套:
import pytest@pytest.fixture
def first_entry():return "a"@pytest.fixture
def order(first_entry):return [first_entry]def test_string(order):order.append("b")assert order == ["a", "b"] # 断⾔
7.3、请求多个fixture:
import pytestclass Fruit:
def __init__(self, name):self.name = name
def __eq__(self, other): #重写的比较方法 在比较两个Fruit对象是否相等时被调用return self.name == other.name@pytest.fixture
def my_fruit():return Fruit("apple") #类对象 就会调用Fruit类里面的初始化函数@pytest.fixture
def fruit_basket(my_fruit):return [Fruit("banana"), my_fruit] #类对象放在列表里面def test_my_fruit_in_basket(my_fruit, fruit_basket):assert my_fruit in fruit_basket #这里是在比较两个类对象 调用__eq__方法
上面的在测试方法中传入函数名作为参数时,会先执行传入的函数,这就实现了前置。那么后置怎么实现呢?
7.4、yield fixture
@pytest.fixture
def operator():print("前置操作:数据的初始化")yieldprint("后置操作:数据的清理")def test_01(operator):print("第一个测试用例")
由结果可以看出,先执行operator中yield之前的部分,再执行调用operator的函数,执行完函数再执行yield的后半部分,就完成了前后置操作。和前面介绍的setup_method和teardown_method效果一样
yield可以返回数据。那么可以看出,是先执行前部分+yield,再执行测试用例,再执行后部分
@pytest.fixture
def operator():print("数据的初始化")yield 100print("数据的清理")
def test_01(operator):print(100 + operator)
文件:打开——读模式:读文件、写模式:写文件——关闭
@pytest.fixture
def file_read():print("打开文件句柄")fo = open("case/test.txt","r",encoding="utf-8")yield foprint("关闭文件句柄")fo.close()@pytest.fixture
def file_write():print("打开文件句柄")fo = open("case/test.txt","w",encoding="utf-8")return fo# yield fo# print("关闭文件句柄")# fo.close()def test_file(file_read,file_write):w = file_writew.write("我们")w.close()r = file_readstr = r.read()print(str)
7.5、带参数的fixture
pytest.fixture(scope='', autouse='',params='',ids='',name='')
1、scope:控制fixture的作用范围,决定了fixture的生命周期
1)funtion(默认)
2)class:在同一个测试类中共享(类中的第一个测试函数调用fixture函数的前部分,最后一个函数调用后部分、如果有第二个类的话和第一个类相同)---(初始化--第一个用例、第二个用例---清理--初始化---第一个用例、第二个用例--清理)
3)module:在同一个测试模块中共享这个fixture(一个文件里)(要加一个conftest.py文件)----(一个文件中有两个类,只会在第一个类之前执行初始化,在第二个类之后执行清理)---(初始化--第一个用例、第二个用例--第一个用例、第二个用例--清除)(同一个文件中的)
4)session:整个测试会话中共享这个fixture初始化--第一个用例、第二个用例--第一个用例、第二个用例--清除)---将cnftest.py文件涉及到的所有的文件整合到一起
2、autouse:默认值为false。设置为ture时,就不需要进行显示传递
3、params:用于参数化fixture,支持列表传入。每个参数都会使fixture执行一次。
4、ids:与params配合使用,给参数取名字
5、name:给fixture取名字
当测试用例在不同的文件里时,需要将fixture放到配置文件里,实现多个文件共享。
scope = "moudle"、scope = "session"时可用于实现全局的前后置应用,这里需要多一个文件配合,conftest.py和@pytest.fixture结合使用实现全局变量的前后置应用
conftest.py名称时固定的不能改变
每个conftest.py文件都会对其所在的目录及其子目录下的测试模块生效
在不同模块的测试中需要用到conftest.py的前后置功能时,不需要做任何的import导入操作。
可以在不同的.py文件中使用同一个fixture文件
scope = "moudle"、scope = "session"区别:module每个都进行初始化和清理,session第一个进行初始化,最后一个进行清理
params实现参数化和@pytest.mark.parametrize实现参数化的区别:
parametrize更适合简单场景,而fixture更适合需要动态数据和资源的复杂场景。
@pytest.mark.parametrize("参数名",(参数1,参数2)
测试函数
@pytest.fixture(params=[1,2,3])
def data_provider(request): #fixture标记的参数里面参数叫做requestreturn request.param #通过request读取paramsdef test_data(data_provider):print(data_provider)
注意:要写为request.param而不是request.params