Pytest接口自动化1-从入门到进阶实战
一、为什么要用Pytest来做接口自动化
1.Pytest的用途及优点
Pytest目前是自动化业界非常主流的一个自动化测试框架,它本质是Python的一个第三方单元测试库。和unittest一样,主要是用来管理自动化测试用例的执行的,比如用例执行,用例分组,执行日志输出等等。
Pytest的优点:
1.他可以自动的识别测试模块(测试文件)、测试类以及测试函数,规则很简单:
文件名:格式为test _ *.py或* _test.py的文件
类:Test开头
测试函数/方法:拥有test前缀的测试函数或方法
2.模块化夹具fixture可用来管理各类测试资源
3.对unittest完全兼容
我们知道在pytest出来之前,unittest应用也极为广泛,因此完全兼容unittest,在想要对自动化框架升级的时候可以节省不少开发成本。
4.Pytest是最能 装“插”的开源单元测试框架
https://docs.pytest.org/en/latest/reference/plugin_list.html
目前pytest已经有900多个插件,每天都会新增1到2个插件。著名的插件有:
pytest-allure、pytest-rerunfailures、pytest-xdist等等
插件能力:集成功能强大的测试报告组件,实现失败重跑,并发用例执行等
插件的安装也非常简单,基本上就是:pip install 插件名
5.pytest安装
1)安装命令
pip install pytest
2)验证安装
pytest --version
3)官方文档
https://docs.pytest.org/en/latest/contents.html
以上这些,充分说明pytest是一个简单、易用并且功能强大的用例管理框架。
二、接口测试简介
接口组成元素:
1.接口地址(url+端口+路径)
2.接口请求方式: post get delete put...
3.接口请求参数
4.响应数据
这里演示使用postman完成登陆接口调用:

然后我们看到登陆成功后,响应报文里头有个token,这个东西是干嘛的呢,简单介绍一下:
token用途简介
登陆系统 = 进医院大门
token = 绿码
拥有了绿码才有进入医院的权限 = 拥有token才能访问系统内部的各个页面
没有绿码,不允许进入任何门诊 = 没有token,即使你拥有接口的链接,响应也会提示你鉴权失败
以上只是接口最简单的一个入门介绍,要真正的学好接口测试,要学的东西很多,比如必须熟悉的入门级的基础理论:接口通信原理、HTTP网络协议、接口鉴权机制等等,一节课肯定是没法熟悉接口自动化的,必须系统的去学习才能完整的掌握
三、用Python代码实现接口测试
相对于工具,用python做接口自动化的优势:
扩展性更强,更灵活
python 各种封装、调用
可集成各种库和工具,满足各种需求
allure 报告
jsonpath 报文解析
jenkins 持续集成
。。。
代码实操:Pytest_intf_advance/base_intf/demo.py
PS:这里的接口地址是自己本地搭建的一个接口测试服务,仅供大家参考,后期有时间再弄个免费的公网接口服务给大家使用
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @File : demo.py
import json
import jsonpath
import requests
from Pytest_intf_advance.base_intf.api_key import ApiKey
# 接口请求的模拟
# 数据的生成
data = {
'username': 'admin',
'password': '123456'
}
# 接口的地址
url = 'http://127.0.0.1:5000/api/login'
# 将数据传递到对应的接口地址,来实现一次该接口的请求下发并返回响应结果:定义对应的请求方法
res = requests.post(url=url, json=data)
# 输出响应结果:编译后的内容
print(res.text)
# 返回报文中的某个key,比如msg来获取相应的值
print(type(res.text))
# 输出响应结果:字典类型
print(res.json())
print(type(res.json()))
# 接口断言
assert 'success' == res.json()['msg']
# 接口断言,嵌套字典,{key:value,key:{key:value}}
# assert 'changsha' == res.json()['city']
assert 'changsha' == res.json()['adress']['city']
# 那么这里我们就可以用到jsonpath库,来简化取值操作
# jsonpath获取数据的表达式:成功则返回list,失败则返回false
# loads是将json格式的内容转换为字典的格式
# jsonpath接收的是dict类型的数据
value_list = jsonpath.jsonpath(res.json(), '$..{0}'.format('city'))
print(value_list)
value = value_list[0]
print(value)
assert 'changsha' == value
# 就有点繁琐,因此我们引入接口关键字封装,来简化代码,封装api_key
# 实例化工具类
ak = ApiKey()
assert 'changsha' == ak.get_text(res.text,'city')
print(ak.get_text(res.text,'city'))
代码执行输出信息:
{
"adress": {
"city": "changsha"
},
"httpstatus": 200,
"info": {
"age": 18,
"name": "admin"
},
"msg": "success",
"token": "23657DGYUSGD126731638712GE18271H"
}
<class 'str'>
{'adress': {'city': 'changsha'}, 'httpstatus': 200, 'info': {'age': 18, 'name': 'admin'}, 'msg': 'success', 'token': '23657DGYUSGD126731638712GE18271H'}
<class 'dict'>
['changsha']
changsha
changsha
Process finished with exit code 0
四、接口关键字封装
代码实操:Pytest_intf_advance/base_intf/api_key.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @File : api_key.py
"""
这是接口关键字驱动类,用于提供自动化接口测试的关键字方法。
主要实现常用的关键字内容,并定义好所有的参数内容即可
"""
import json
import allure
import jsonpath
import requests
class ApiKey:
# 基于jsonpath获取数据的关键字:用于提取所需要的内容
def get_text(self,data,key):
# jsonpath获取数据的表达式:成功则返回list,失败则返回false
# loads是将json格式的内容转换为字典的格式
# jsonpath接收的是dict类型的数据
dict_data = json.loads(data)
value = jsonpath.jsonpath(dict_data,'$..{0}'.format(key))
return value[0]
# get请求的封装:因为params可能存在无值的情况,存放默认None
def get(self,url,params=None,**kwargs):
return requests.get(url=url,params=params,**kwargs)
#post请求的封装:data也可能存在无值得情况,存放默认None
def post(self,url,data=None,**kwargs):
return requests.post(url=url,data=data,**kwargs)
if __name__ == '__main__':
ak = ApiKey()
data = {
'username': 'admin',
'password': '123456'
}
res2 = ak.post(url='http://127.0.0.1:5000/api/login',json=data)
print(res2.text)
代码执行输出信息:
{
"adress": {
"city": "changsha"
},
"httpstatus": 200,
"info": {
"age": 18,
"name": "admin"
},
"msg": "success",
"token": "23657DGYUSGD126731638712GE18271H"
}
那么我们第一个接口自动化代码就写完了,我们可以看到,上述代码是不是很松散。没有以一个用例的形式来进行,那么这时候我们就可以上Pytest了
五、用Pytest框架编写接口自动化测试代码
1.基本用例组织
pytest用例运行测规则很简单,从上往下,完全按你放置的顺序来执行
代码实操:Pytest_intf_advance/pytest_intf/case/test_shopXo.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @File : test_shopXo.py
import pytest
import requests
from Pytest_intf_advance_V2.base_intf.api_key import ApiKey
class Test_ApiCase():
# 登陆接口用例
def test_login(self):
url = 'http://127.0.0.1:5000/api/login'
userInfo = {
'username': 'admin',
'password': '123456'
}
res = self.ak.post(url=url, json=userInfo)
print(res.text)
# 获取响应中的结果,用于校验是否成功
msg1 = self.ak.get_text(res.text, 'msg')
print(msg1)
assert msg1 == 'success'
# 查询用户信息接口
def test_getuserinfo(self,token_fix):
# 1.获取工具类、token
ak, token = token_fix
# 2.查询个人用户信息
url = 'http://127.0.0.1:5000/api/getuserinfo'
headers = {
'token': token
}
res1 = ak.get(url=url, headers=headers)
print(res1.text)
name = ak.get_text(res1.text, 'nikename')
assert "风清扬" == name
if __name__ == '__main__':
# -s参数,在控制台输出打印信息
# -v参数,在控制台输出详细信息
pytest.main(['-s','-v','test_shopXo_02.py'])
代码执行输出信息:
============================= test session starts =============================
platform win32 -- Python 3.8.10, pytest-6.1.2, py-1.9.0, pluggy-0.13.1 -- D:\Python38\python.exe
cachedir: .pytest_cache
metadata: {'Python': '3.8.10', 'Platform': 'Windows-10-10.0.19044-SP0', 'Packages': {'pytest': '6.1.2', 'py': '1.9.0', 'pluggy': '0.13.1'}, 'Plugins': {'allure-pytest': '2.8.11', 'forked': '1.1.3', 'html': '3.0.0', 'metadata': '1.8.0', 'ordering': '0.6', 'parallel': '0.1.0', 'rerunfailures': '9.1.1', 'xdist': '1.31.0'}, 'JAVA_HOME': 'C:\\Program Files\\Java\\jdk1.8.0_152'}
rootdir: D:\Pytest_intf_advance_V2\pytest_intf\case
plugins: allure-pytest-2.8.11, forked-1.1.3, html-3.0.0, metadata-1.8.0, ordering-0.6, parallel-0.1.0, rerunfailures-9.1.1, xdist-1.31.0
collecting ... collected 2 items
test_shopXo.py::Test_ApiCase::test_login {
"adress": {
"city": "changsha"
},
"httpstatus": 200,
"info": {
"age": 18,
"name": "admin"
},
"msg": "success",
"token": "23657DGYUSGD126731638712GE18271H"
}
success
PASSED
test_shopXo.py::Test_ApiCase::test_getuserinfo {
"data": [
{
"nikename": "王五",
"openid": "UEHUXUXU78272SDSassDD",
"userbalance": 5678.9,
"userid": 17890,
"username": "admin",
"userpoints": 4321
}
],
"httpstatus": 200
}
PASSED
============================== 2 passed in 0.30s ==============================
Process finished with exit code 0
2.接口关联
实现接口关联有多种方式:
1)和工具类相似使用setup_class,用的时候加个self就可以了
2)公共变量可以定义一个公共变量放到类里,这样类中的所有用例都可以读取到了
那么上面这两种方式,其实是有局限性的,就是只能在单个类或者单个文件中使用,如果需要在整个项目的多个测试文件中使用,就不行了,现在就给大家着重介绍第三种方式,来实现项目级的token预置
六、fixture+conftest实现项目级token预置
接下来,我们先讲讲fixture,官方的介绍比较复杂,这边我个人总结了下。
1.pytest之fixture介绍
fixture是pytest提供的一个简化的装饰器,可以轻松的复用已定义的函数逻辑(比如登陆,获取token,环境数据预置)等操作。
官方介绍:
https://docs.pytest.org/en/latest/explanation/fixtures.html#about-fixtures
概念很简单,关键是如何用,下面给大家编写一个fixture快速入门的例子
代码实操:Pytest_intf_advance/base_intf/test_fix/test_quick_exam.py
#coding=utf-8
import pytest
# 通过@pytest.fixture声明这个函数为fixture
# 用例前置
@pytest.fixture
def first_fix():
# 此处假设有代码逻辑几十行,比如执行登陆操作,获取token之类的
# 返回一个list
return ["a"]
# 将已声明为fixture的函数,填写在参数中,这样fixture函数会在该函数调用之前调用
# 测试用例
def test_string(first_fix):
# 用例步骤
# 使用first_fix返回的list添加元素
first_fix.append("b")
print(first_fix)
if __name__ == '__main__':
pytest.main(['-s'])
代码执行输出信息:
============================= test session starts ============================= platform win32 -- Python 3.8.10, pytest-6.1.2, py-1.9.0, pluggy-0.13.1 rootdir: D:\Pytest_intf_advance_V2\base_intf\test_fix plugins: allure-pytest-2.8.11, forked-1.1.3, html-3.0.0, metadata-1.8.0, ordering-0.6, parallel-0.1.0, rerunfailures-9.1.1, xdist-1.31.0 collected 3 items test_quick_exam.py ['a', 'b'] . test_conftest_01\doc1\test_case01.py 开始执行登陆操作 用例一 . test_conftest_01\doc2\test_case02.py 开始执行登陆操作 用例二 . ============================== 3 passed in 0.07s ============================== Process finished with exit code 0
以上就是fixture最基本的概念和用法。这时候,可能有人就想问了,那我想在其他文件中使用这个定义好的fixture怎么办呢?那这就要介绍下fixture的好基友,conftest.py配置文件了
2.conftest.py
conftest.py是pytest特有的本地测试配置文件,在这个文件中定义的Fixture可以在项目中多个文件使用,conftest.py文件名称是固定的,pytest会自动识别该文件,只作用于它所在的目录及子目录。
同样的,编写一个快速入门的代码例子:
代码实操:多文件代码,先看代码结构

Pytest_intf_advance/base_intf/test_fix/test_conftest_01/conftest.py
#coding=gbk
import pytest
@pytest.fixture()
def fix1():
print("\n开始执行登陆操作")
Pytest_intf_advance/base_intf/test_fix/test_conftest_01/doc1/test_case01.py
#coding=gbk
import pytest
def test_case02(fix1):
print("用例一")
Pytest_intf_advance/base_intf/test_fix/test_conftest_01/doc2/test_case02.py
#coding=gbk
import pytest
def test_case02(fix1):
print("用例二")
Pytest_intf_advance/base_intf/test_fix/test_conftest_01/main_run.py
#coding=gbk
import pytest
if __name__ == '__main__':
pytest.main(['-s'])
运行main_run.py文件
代码输出:
collected 2 items doc1\test_case01.py 开始执行登陆操作 用例一 . doc2\test_case02.py 开始执行登陆操作 用例二 . ============================== 2 passed in 0.05s ============================== Process finished with exit code 0
fixture+conftest的搭配是非常的有用的,接下来我们来在接口自动化中实战一把,来展现一下这对黄金组合的实力
3.接口中应用fix+conftest实现项目级token预置
1)先在conftest中定义fixture
初始化工具类,登陆,并返回工具类对象和token值
代码实操:多文件代码,先看代码结构

Pytest_intf_advance_V2/pytest_intf/conftest.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @File : conftest.py
from random import random
from Pytest_intf_advance.base_intf.api_key import ApiKey
import pytest
# @pytest.fixture(scope='session')
# def token_fix():
# a = random()
# print(a)
# return a
#项目级fix,整个项目只初始化一次
@pytest.fixture(scope='session')
def token_fix():
# 初始化工具类
ak = ApiKey()
# 定义访问链接‘
url = 'http://127.0.0.1:5000/api/login'
# 定义请求用户数据
userInfo = {
'username': 'admin',
'password': '123456'
}
# 发送post请求
res = ak.post(url=url,json = userInfo)
# 获取token
token = ak.get_text(res.text,'token')
# 返回多个值
return ak,token
Pytest_intf_advance_V2\pytest_intf\api_keyword\api_key.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @File : api_key.py
"""
这是接口关键字驱动类,用于提供自动化接口测试的关键字方法。
主要实现常用的关键字内容,并定义好所有的参数内容即可
"""
import json
import allure
import jsonpath
import requests
class ApiKey:
# 基于jsonpath获取数据的关键字:用于提取所需要的内容
def get_text(self,data,key):
# jsonpath获取数据的表达式:成功则返回list,失败则返回false
# loads是将json格式的内容转换为字典的格式
# jsonpath接收的是dict类型的数据
dict_data = json.loads(data)
value = jsonpath.jsonpath(dict_data,'$..{0}'.format(key))
return value[0]
# get请求的封装:因为params可能存在无值的情况,存放默认None
def get(self,url,params=None,**kwargs):
return requests.get(url=url,params=params,**kwargs)
#post请求的封装:data也可能存在无值得情况,存放默认None
def post(self,url,data=None,**kwargs):
return requests.post(url=url,data=data,**kwargs)
if __name__ == '__main__':
ak = ApiKey()
# res = ak.get(url='http://127.0.0.1:5000/api/getuserinfo',timeout=0.1)
# print(res.text)
data = {
'username': 'admin',
'password': '123456'
}
res2 = ak.post(url='http://127.0.0.1:5000/api/login',json=data)
print(res2.text)
Pytest_intf_advance_V2\pytest_intf\case\test_shopXo_02.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @File : test_shopXo.py
import pytest
import requests
from Pytest_intf_advance_V2.base_intf.api_key import ApiKey
class Test_ApiCase():
# 查询用户信息接口
def test_getuserinfo(self,token_fix):
# 1.获取工具类、token
ak, token = token_fix
# 2.查询个人用户信息
url = 'http://127.0.0.1:5000/api/getuserinfo'
headers = {
'token': token
}
res1 = ak.get(url=url, headers=headers)
print(res1.text)
name = ak.get_text(res1.text, 'nikename')
assert "张三" == name
if __name__ == '__main__':
# -s参数,在控制台输出打印信息
# -v参数,在控制台输出详细信息
pytest.main(['-s','-v','test_shopXo_02.py'])
代码输出:
collecting ... collected 1 item
test_shopXo_02.py::Test_ApiCase::test_getuserinfo {
"data": [
{
"nikename": "张三",
"openid": "UEHUXUXU78272SDSassDD",
"userbalance": 5678.9,
"userid": 17890,
"username": "admin",
"userpoints": 4321
}
],
"httpstatus": 200
}
PASSED
============================== 1 passed in 0.20s ==============================
Process finished with exit code 0
通过fixture+conftest 这套组合,可以方便的在多个文件中使用同一个token,可以在项目内实现更大范围内的接口关联。
本文来自博客园,作者:测试老宅男扶摇,转载请注明原文链接:https://www.cnblogs.com/cekailsf/p/15919246.html
浙公网安备 33010602011771号