Python+Request
1、环境安装
学习python+request需要安装的环境有:requests、pytest-html、typing、Pytest
使用pip install命令安装
pip install requests
pip install pytest-html
pip install typing
pip install Pytest
也可以使用离线安装,将第三方库下载好,解压放在python解释器的Lib里面的的site-packages目录里面,如果没有的site-packages就自己创建一个
检查第三方库是否安装好
点击settings-->project:xxx-->python interpreter-->查看有没有第三方库
2、requests
2.1request.get()
使用request.get()发送http get请求之后会返回一个response对象
import requests
resp = requests.get("https://www.baidu.com")
print(resp.status_code) # 200
返回的response对象里面有系列的属性和方法,下面列举出一些常用的属性和方法
常用属性
属性 类型 描述
status_code int HTTP 状态码(如 200, 404)
url str 最终响应的 URL(含重定向)
headers dict 响应头(不区分大小写)
text str 响应内容(自动解码的文本)
content bytes 原始二进制响应内容
encoding str 响应内容的编码(可修改)
cookies RequestsCookieJar 服务器返回的 Cookies
reason str 状态码描述(如 "OK", "Not Found")
elapsed timedelta 请求耗时
request PreparedRequest 关联的请求对象
常用方法
json()
解析 JSON 响应为 Python 字典/列表
2.2requests.post
使用request.post()发送http post请求之后会返回一个response对象
需要填入的参数有:请求地址,请求头,请求体
请求地址url是字符串格式,请求头和请求体都是字典的格式
这是requests.post方法的源码
def post(url, data=None, json=None, **kwargs):
r"""Sends a POST request.
:param url: URL for the new :class:`Request` object.
:param data: (optional) Dictionary, list of tuples, bytes, or file-like
object to send in the body of the :class:`Request`.
:param json: (optional) A JSON serializable Python object to send in the body of the :class:`Request`.
:param \*\*kwargs: Optional arguments that ``request`` takes.
:return: :class:`Response <Response>` object
:rtype: requests.Response
"""
return request("post", url, data=data, json=json, **kwargs)
url是位置参数,一般用来传post的请求地址,data和json是默认参数,**kwargs是不定长关键字参数,具体谁来传请求头和请求体的内容,需要看后端开发是怎么写的,没有固定的格式。例如
import requests
url = "http://172.31.52.83:38000/api2/auth-token/"
headers = {"Content-Type": "application/x-www-form-urlencoded"}
body = {"username": "seafile@admin.com", "password": "admin"}
resp2 = requests.post(url, data=body, headers=headers)
print(resp2.status_code)
post方法的返回结果response的常用属性和方法同get方法,就不再赘述了
3、接口关联
关于接口关联,有两种情况,一种是不同请求在一个脚本中,一种是不同的请求在不同脚本中。如果在同一个脚本中,直接从上游接口提取出来然后在下游接口使用就行,如果是在不同的脚本中,可以使用测试框架的fixture(夹具)来实现
# 在同一个脚本中
import requests
url = "http://172.31.52.83:38000/api2/auth-token/"
headers = {"Content-Type": "application/x-www-form-urlencoded"}
body = {"username": "seafile@admin.com", "password": "admin"}
resp1 = requests.post(url, data=body, headers=headers)
url = "http://172.31.52.83:38000/api2/account/info/"
headers = {"Authorization": "token "+resp1.json()["token"]}
resp2 = requests.get(url, headers=headers)
print(resp2.text)
print(resp2.status_code)
# 在不同的脚本中实现接口关联
1111,以后补充
3.1json提取
如果返回的结果是json格式,那么可以直接用json的方式提取出来
例如这里要提取出来上一个接口的响应结果的token值,响应的结果是json格式
token = resp1.json()["token"]
3.2正则表达式提取
如果返回的结果不是json格式,是html,xml或者纯文本的格式,那么可以用正则表达式提取。关于正则表达式的语法,不是重点,可以自行ai搜索学习
import re
import requests
url = "http://172.31.52.83:38000/api2/auth-token/"
headers = {"Content-Type": "application/x-www-form-urlencoded"}
body = {"username": "seafile@admin.com", "password": "admin"}
resp1 = requests.post(url, data=body, headers=headers)
reg = "{\"token\":\"(.*?)\"}"
token = re.match(reg, resp1.text)[1]
print(token)
4、断言
断言基本语法:assert 实际结果==预期
常见的断言方法:
assert 实际结果 == 预期结果
assert 实际结果 != 预期结果
assert 实际结果 > 预期结果
assert 实际结果 >= 预期结果
assert 实际结果 < 预期结果
assert 实际结果 <= 预期结果
assert 实际结果 in 预期结果
assert 实际结果 not in 预期结果
assert 实际结果 True
assert 实际结果 False
assert 实际结果 is 预期结果
assert 实际结果 is not 预期结果
一般断言的内容:
1.断言响应头
2.断言响应体
3.断言状态码
4.断言响应时间
5、Pytest
5.1默认命名规则
1. 包名(Package Name)
通常命名为 tests 或 test_ 开头
要求:目录中需包含 __init__.py 文件(可为空),但 pytest 7.0+ 支持无 __init__.py 的目录
2. 模块名(Module Name)
测试文件必须以以下两种方式之一命名:
test_*.py
*_test.py
非此类命名的文件会被 pytest 忽略
3. 测试类名(Class Name)
测试类必须以 Test 开头(驼峰式命名,首字母大写)
类名不能有 __init__ 构造函数(否则 pytest 会跳过该类)
4. 测试方法/函数名(Test Function/Method Name)
所有测试用例必须以 test_ 开头。
测试方法(类内):def test_*(self): ...
测试函数(类外):def test_*(): ...
5. Fixture 函数名(Fixture Functions)
Fixture 函数名无强制前缀,但需用 @pytest.fixture 装饰器标记
6. 插件与钩子函数(Plugins and Hooks)
插件模块名:通常以 pytest_ 开头(如 pytest_custom_plugin.py)
钩子函数名:需符合 pytest 的钩子规范(如 pytest_collection_modifyitems)
7. 特殊文件名
conftest.py:用于共享 Fixture 和钩子,必须使用此名称。
__init__.py:标记测试目录为包(旧版本要求,pytest 7.0+ 可选)
5.2Pytest文件执行
在python中,文件的执行方式有两种,一种是直接以脚本的方式执行,一种是以模块的方式导入另一个.py文件中执行。当涉及到pytest库的时候,如果直接执行pytest的文件,无法确定到底是以哪一种方式执行的,因为pytest相关的文件是由pytest库调用执行的。为了确保pytest文件每次都能由pytest库调用执行,建议使用导入模块的方式执行
import pytest
if __name__ == '__main__':
pytest.main(['-sv', './test_api.py', '--html=my_case.html'])
参数解释
第一个参数:
-s:显示程序中的print/logging输出
-V:丰富信息模式,输出更详细的用例执行信息
-q:安静模式,不输出环境信息
-k:关键字匹配,用and区分:匹配范围(文件名、类名、函数名)
第二个参数./test_case:
表示要执行的测试文件的目录路径,这个就是执行test_case目录下的所有测试用例代码文件。
第三个参数--html=my_case.html:
固定式内容,表示最后执行完成后生成pytest的原生报告html文件,方便后续的持续集成等。
控制台会出现这样的结果,显示通过的断言数量和失败的断言数量警告的断言数量。注意!!!如果你的包,模块,类,函数,方法的命名不规范,pytest收集不到测试用例,就会报错
5.3Pytest前后置
前后置处理,主要是在测试用例执行之前和执行之后需要做的操作内容
前置函数 setup_function()
后置函数 teardown_function()
如果你的模块中写了前后置函数(注意命名规范)那么模块中的所有函数级别的测试用例都会被前后置函数包裹起来。执行一个测试用例之前先执行一遍前置函数,再执行测试用例,最后执行一遍后置函数
前置类方法 setup_class(self)
后置类方法 teardown_class(self)
如果你的测试类中写了前后置类方法(注意命名规范)那么模块中的所有测试类都会被前后置类方法包裹起来。执行一个测试类之前先执行一遍前置类方法,再执行测试类里的测试用例,最后执行一遍后置类方法
前置方法 setup_method(self)
后置方法 teardown_method(self)
如果你的测试类中写了前后置方法(注意命名规范)那么测试类中的所有测试方法都会被前后置方法包裹起来。执行一个测试方法之前先执行一遍前置方法,再执行测试方法,最后执行一遍后置方法
同样的,还有模块的前后置 setup_module(),teardown_module(),只是不常用
例如,在模块中写
def setup_function():
print("我在测试方法之前执行")
def teardown_function():
print("我在测试方法之后执行")
def test_001():
print("测试用例执行")
assert resp1.status_code == 200
然后在将它作为模块导入执行。如果setup_function和teardown_function
不是前后置函数,那么控制台输出的顺序应该是从上倒下按顺序输出的;如果
setup_function和teardown_function是前后置函数,那么应该先输出
"我在测试方法之前执行",再输出,"测试用例执行",最后输出
"我在测试方法之后执行"
实际的结果是

说明确实是前后置函数
5.4csv参数化
1、准备一个csv文件
新建一个.txt的文本文档,然后输入键值对,中间用英文的逗号隔开,注意用utf-8的格式保存,不要有多余的空行

保存后更改后缀,将.txt改成.csv

记录文件的存放路径
2、写一个读取csv文件的函数
例如,
import csv
def get_data_from_csv(path):
data_list = []
file = csv.reader(open(path, encoding="utf-8"))
for row in file:
data_list.append(row)
else:
return data_list
返回的data_list列表是这样的一个列表
[['韩立', '18'], ['萧炎', '19'], ['唐三', '34'], ['王林', '25'], ['白小纯', '29']]
中括号里面套中括号,读取csv文件的函数,每次读取一行,将读取到的一行的内容放在中括号中,然后将所有的中括号放在一个大的中括号中
事实上不管是读取的什么文件(例如json文件),只要能最后生成一个这样的列表都可以进行参数化
3、对测试用例进行参数化
使用装饰器(语法糖的一种)@pytest.mark.parametrize()进行参数化
@pytest.mark.parametrize里面需要传两个参数,一个形参,一个实际的csv数据,形参必须和测试用例的形参一致。我这里给了两种写法,一种是用自己封装的读取csv的函数,一中是在装饰器中直接写参数
import pytest
from comn.tool import get_data_from_csv
print(get_data_from_csv("./data.csv"))
@pytest.mark.parametrize("a,b", get_data_from_csv("./data.csv"))
def test_001(a, b):
print("第一个参数:" + str(a))
print("第二个参数:" + str(b))
@pytest.mark.parametrize("a,b", [[1, 3], [4, 5], [6, 7]])
def test_001(a, b):
print("第一个参数:" + str(a))
print("第二个参数:" + str(b))
5.5内置标记
pytest标记跳过用例的方法有两种:
无条件跳过
@pytest.mark.skip(reason='断言会失败,不让执行')
def test_002():
print('函数:我是测试用例test002')
assert 1 == 2
有条件跳过
@pytest.mark.skipif(1 != 1, reason='条件不成立,用例不跳过')
def test_001():
print('函数:我是测试用例test001')
assert 1 == 1
@pytest.mark.skipif(1 == 1, reason='条件成立就跳过')
def test_003():
print('函数:我是测试用例test003')
assert 1 <= 0
pytest标记失败测试用例
标记预期会出现异常或者失败的测试用例,只有出现异常才对,不出现异常反而不对
正常预期失败:
@pytest.mark.xfail(reason='标记用例失败')
def test_004(self):
print('实例方法:我是测试用例test004')
assert 1 > 2
# 1 xfailed
预期意外通过:
@pytest.mark.xfail(reason='标记用例失败')
def test_005(self):
print('实例方法:我是测试用例test005')
assert 2 == 2
# 1 xpassed
6、夹具fixture
夹具fixture装饰器的作用和setup/teardown以及setup_class和teardown_class一样,都是在测试用例或者测试类前后执行,但是fixture更加灵活。有时候,只有部分的用例需要用到前后置,但是如果使用前置函数和后置函数的话,会导致所有的用例都加上前后置,而使用fixture装饰器,就可以很灵活的只给部分用例使用前后置
fixture装饰器的基本语法
@pytest.fixture(scope="作用域", autouse="是否自动使用:True/False")
fixture装饰器会在用例执行之前、之后,自动运行代码
fixture装饰器通常和yield关键字一起使用,函数中有yield关键字,那么它就是生成器(生成器属于函数的一种类型)
import pytest
@pytest.fixture
def func():
print('前置:在用例执行之前')
yield 123 # 返回值
print('后置:在用例执行之后')
对于一般函数而言,yield关键字会暂停函数并返回值,当函数再次被调用的时候会从yeild之后的代码开始执行,直到再次遇到yeild暂停或者函数结束。但是当yeild关键字和fixture装饰器一起用的时候,在调用被fixture装饰的函数的时候,yeild前面的代码会作为前置,后面的代码会作为后置。
如果想要手动调用被fixture装饰器装饰的函数,有两种调用方式
一种是写函数名不加括号直接调用
另一种是使用@pytest.mark.usefixtures标记
@pytest.mark.usefixtures标记需要传如被调用的函数的字符串形式的名字。
直接调用可以接收结果,使用@pytest.mark.usefixtures标记调用不能接收结果
import pytest
@pytest.fixture
def func():
print('前置:在用例执行之前')
yield 123 # 返回值
print('后置:在用例执行之后')
# 第一种使用方式
def test_001(func):
print('我是测试用例我正在执行')
print('fixture返回的结果是',func)
# 第二种使用方式
@pytest.mark.usefixtures('func')
def test_002():
print('我是测试用例我正在执行')
print('fixture返回的结果是', func) # 这里想要获取返回值是获取不道的
# 演示直接加括号调用--》错误的方式
def test_003():
print('我是测试用例我正在执行')
print('fixture返回的结果是', func()) # 错误
对于方法1和方法2的调用,得到的结果是
因此,第二种调用方式是获取不到返回结果的
如果想要自动调用fixture夹具装饰的内容,可以给fixture装饰器设置作用域和自动调用
@pytest.fixture(scope='class', autouse=True)
def func():
print('用例之前执行')
yield 15
print('用例之后执行')
def test_001(func):
print('测试用例1正在执行')
print(f'测试用例1执行了{func}分钟')
def test_002():
print('测试用例2正在执行')
print(f'测试用例2执行了{func}分钟')
class TestA:
def test_003(self):
print('我是测试用例003号')
print('我正在执行003')
print('我执行结束啦003')
def test_004(self):
print('我是测试用例004号')
print('我正在执行004')
print('我执行结束啦004')
执行的结果是
fx.py::test_001 用例之前执行
测试用例1正在执行
测试用例1执行了15分钟
PASSED用例之后执行
fx.py::test_002 用例之前执行
测试用例2正在执行
测试用例2执行了<pytest_fixture(<function func at 0x000002C49EDAC550>)>分钟
PASSED用例之后执行
fx.py::TestA::test_003 用例之前执行
我是测试用例003号
我正在执行003
我执行结束啦003
PASSED
fx.py::TestA::test_004 我是测试用例004号
我正在执行004
我执行结束啦004
PASSED用例之后执行
由此我给出作用域的作用规则(我实验的结果,如果有问题还请不吝指正):
1、function:给函数和方法都使用夹具
2、method:给方法使用夹具
3、class:给类使用夹具,以及类外面的函数使用夹具
4、module:给整个模块使用夹具
5、package:给整个包使用夹具
6、session:在pytest的整个运行期间使用一次夹具
7、日志log
(以后整理,之前封装了日志工具)
日志需要一个插件pytest-result-log
安装命令是
pip install pytest-result-log
新建pytest.ini配置文件:配置以下内容
在项目下新建pytest.ini文件
要加[pytest]申明
在封装接口脚本中所有输出的信息,改成日志记录:
pip install pytest-result-log
[pytest]
log_file = ./logs/pytest.log
log_file_level = info
log_file_format = %(levelname)-8s %(asctime)s [%(name)s:%(lineno)s] : %(message)s
log_file_date_format = %Y-%m-%d %H:%M:%S
; 记录用例执行结果
result_log_enable = 1
; 记录用例分割线
result_log_separator = 1
;分割线等级
result_log_level_separator = warning
;异常信息等级
result_log_level_verbose = info
8、测试报告allure
(根据pytest的生成报告的结果进行一个报告的美化)
更多推荐

所有评论(0)