python+requests接口测试

第一章 HTTP网络协议

在接口的测试过程中,所有的数据交互都是基于网络协议来进行交互的。目前业内最为主流的是HTTP网络协议。占据有90%左右比例
在系统的交互过程中,HTTP网络协议是如何实现交互的?
目前在计算机的数据交互中最为有效的一种交互形式。

1.1 网络协议下的交互

网络协议下的交互总计4个步骤:
1.基于访问的url进行识别,与服务器进行连接的建立
解析url,识别到服务器的IP地址。基于服务器进行通信与连接。最终建立一条C-S的连接通道。
2.基于C端请求的数据内容,进行一次请求下发。
3.服务器基于请求的内容进行解析,生成响应结果,进行返回。
4.在客户端接收响应结果,并基于代码进行渲染与展示。在HTTP网络协议下,永远都是客户端下发请求,服务器进行响应。
市场上的HTTPS就是HTTP网络协议的基础上搭载有SSL协议,合二为一形成HTTPS网络协议。

1.2 基础概念

  1. HTTP网络协议是无状态连接。
    当次下发的请求数据与上一次请求或者下一次请求不会有任何关联。每一次请求都是独立的存在。
    引申新的概念叫做请求的鉴权机制。
    2.连接通道已经建立,为什么又是无状态连接?
    连接通道常规而言,默认是在本次请求结束之后,通道关闭。所以会持续保持无状态连接。
    keep-alive:保持TCP连接通道持续存在,和无状态连接是没有任何关系的。
    3.基于HTTP进行的接口请求内容拆解:
    请求的下发一定是基于URL来实现的。
    URL是什么:
    统一资源定位符。就是用于定位的。,
    网络协议://IP:Port/path?args1=xx&args2=ccc
    https://www.baidu.com:443/s?
    wd=%E8%99%9A%E7%AB%B9&rsv_ spt=1&rsv_ iqid=0xaf9cc6
    ac0006ad85&issp= 1&f=8&rsv
    bp=1&rsV idx=2&ie=utf-
    8&tn=baiduhome_ pg&rsv_ enter=1&rsV_ dI=tb&rsV sug3=7&rsv
    sug1=2&rsv
    sug7=100&rsv_ sug2=0&rsv_ btype=i&prefixsug=
    %25E8%2599%259A%25E7 %25AB%25B9&rsp=6&inputT=719
    &rsV_ sug4=2754

HTTP端口默认是80
HTTPS端口默认是443
接口的ur都是在接口文档中存在的。

1.3 请求方法

请求方法:看接口文档。文档说用什么方法,你就用什么方法)

1.3.1 GET

潜规则下,get请求是需要在urI中明文显示参数的。
一般用于数据的获取
因为urI长度有限制,所以get请求才会有长度限制。

1.3.2 POST

提交数据
因为请求的参数在body中,所以无长度限制。

1.3.3 请求头/体

请求头:
头约定了本次数据在交互的时候的格式及规范。用于添加附加数据内容,一般是cookie
请求体:
传递本次请求的参数,身体搭载数据内容
响应头:
用于规范化,定义需要保存到cookie的值
响应体:
返回本次响应的数据
响应状态:
状态码 200 301 404 500

1.4 鉴权机制

面试的时候关于接口90%概率会问到的问题:
Cookie、Session、Token区别
参考资料:https://segmentfault.com/a/1190000017831088

业务:
实现登录之后的用户,进行个人积分的查询。
1.要先访问登录接口,进行登录成功
2.进行个人积分查询接口,获取当下用户的积分信息。
为了实现业务流程连贯性,所以会添加一个鉴权的机制来
解决HTTP无状态连接的问题。

1.4.1 Cookie(浏览器本地缓存)

早期互联网,主要看咨询,最大的资源耗费就是静态资源,为了提升运行效率(访问速率),在访问的时候,将部分资源缓存到客户端,后续再访问系统的时候,调用客户端的本地缓存,从而实现更加快速的加载。响应头部信息中有set cookies字段。保存到本地,请求发送的时候带上cookies字段,从而实现数据的关联。

1.4.2 Session

是服务器中的一种临时会话机制。当用户请求服务器的时候,服务端会创建一个Sessison, 用于与该用户进行数据交互。基于SessionID的标识来区分所有的session,每一次的请求都会带_上sessionid。如果session太多,会极大占用服务器资源。无法实现多端的数据同步。

1.4.3 Token: (身份令牌)

一般服务端临时保存在数据库中,通过set cookie保存在客户端。

1.5 TCP网络协议(补充说明)

TCP协议是传输层协议,主要解决数据如何在网络中传输,而HTTP是应用层协议,主要解决如何包装数据。
TCP/IP和HTTP协议的关系,从本质上来说,二者没有可比性,我们在传输数据时,可以只使用(传输层)TCP/IP协议,但是那样的话,如果没有应用层,便无法识别数据内容,如果想要使传输的数据有意义,则必须使用到应用层协议,应用层协议有很多,比如HTTP、FTP、TELNET等,也可以自己定义应用层协议。WEB使用HTTP协议作应用层协议,以封装HTTP 文本信息,然后使用TCP/IP做传输层协议将它发到网络上。
Http协议是建立在TCP协议基础之上的,当浏览器需要从服务器获取网页数据的时候,会发出一次Http请求。Http会通过TCP建立起一个到服务器的连接通道,当本次请求需要的数据完毕后,Http会立即将TCP连接断开,这个过程是很短的,所以Http连接是一种短连接,是一种无状态的连接。
python+requests接口测试_第1张图片
特别注意

Seq:是发送方当前报文的顺序号码。
ack:是发送方期望对方在下次返回报文中给回的Seq。

建立连接需要三次握手
第一次握手:客户端向服务端发送连接请求包,标志位SYN(同步序号)置为1,顺序号码为X=0。

第二次握手:服务端收到客户端发过来报文,由SYN=1知道客户端要求建立联机,则为这次连接分配资源。并向客户端发送一个SYN和ACK都置为1的TCP报文,设置初始顺序号码Y=0,将确认序号(ack)设置为上一次客户端发送过来的顺序号(Seq)加1,即X+1 = 0+1=1。

第三次握手:客户端收到服务端发来的包后检查确认号码(ack)是否正确,即第一次发送的Seq加1(X+1=1)。以及标志位ACK是否为1。若正确,服务端再次发送确认包,ACK标志位为1,SYN标志位为0。确认号码(ack)=Y+1=0+1=1,发送顺序号码(Seq)为X+1=1。Server收到后确认号码值与ACK=1则连接建立成功,可以传送数据了。

断开连接需要四次挥手
提醒:中断连接端可以是Client端,也可以是Server端。只要将下面两角色互换即可。
第一次挥手:客户端给服务端发送FIN报文,用来关闭客户端到服务端的数据传送。将标志位FIN和ACK置为1,顺序号码为X=1,确认号码为Z=1。意思是说”我Client端没有数据要发给你了,但是如果你还有数据没有发送完成,则不必急着关闭Socket,可以继续发送数据。所以你先发送ACK过来。”

第二次挥手:服务端收到FIN后,发回一个ACK(标志位ACK=1),确认号码为收到的顺序号码加1,即X=X+1=2。顺序号码为收到的确认号码=Z。意思是说“你的FIN请求我收到了,但是我还没准备好,请继续你等我的消息" 这个时候客户端就进入FIN_WAIT状态,继续等待服务端的FIN报文。

第三次挥手:当服务端确定数据已发送完成,则向客户端发送FIN报文,关闭与客户端的连接。标志位FIN和ACK置为1,顺序号码为Y=1,确认号码为X=2。意思是告诉Client端“好了,我这边数据发完了,准备好关闭连接了。”

第四次挥手:客户端收到服务器发送的FIN之后,发回ACK确认(标志位ACK=1),确认号码为收到的顺序号码加1,即Y+1=2。顺序号码为收到的确认号码X=2。意思是“我Client端知道可以关闭连接了,但是我还是不相信网络,怕 Server端不知道要关闭,所以发送ACK后进入TIME_WAIT状态,如果Server端没有收到ACK则可以重传。Client端等待了2MSL后依然没有收到回复,则证明Server端已正常关闭,那好,我Client端也可以关闭连接了。“(在TIME_WAIT状态中,如果TCP client端最后一次发送的ACK丢失了,它将重新发送。TIME_WAIT状态中所需要的时间是依赖于实现方法的。典型的值为30秒、1分钟和2分钟。等待之后连接正式关闭,并且所有的资源(包括端口号)都被释放。)

为什么关闭的时候却是四次挥(握)手?
因为当Server端收到Client端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。但是关闭连接时,当Server端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉Client端,“你发的FIN报文我收到了”。只有等到我Server端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四步握手

第二章 Requests库接口测试

2.1 接口测试

业内自动化测试兴起之后,最为热门的一项技术。
接口测试是目前而言,互联网世界中测试最为稳定的一项领域对于系统而言后端会比前端更为稳定。
接口测试本身是忽略了前端,直接与服务端来进行测试的一个过程
接口是为系统提供数据服务的,所有前端肉眼可见的数据,基本上都是基于接口来获取的。
前端下发一个请求——服务端接收——基于请求的URL进入到指定的接口——接口进行数据的解析与运算——生成响应结果——原路返回至前端——前端渲染展示在UI层中。
业内常规的接口数据传递的渠道就是所谓的网络协议。业内最为核心的网络协议是HTTP
TCP、HTTPS、UDP
业内所谓的Restful不是网络协议,只是一个规范。
RPC协议下,多元化协议,顶层协议都会封装成HTTP。

2.2 接口测试中最基本的协议知识

2.2.1 HTTP接口的访问:

通过url来进行访问的。

url:统一资源定位符,不同的url代表着不同的接口。url很多时候是包含有参数在内的。

url结构:
网络协议://IP:PORT/PATH?
ARGS=XXXX&ARGS2=ZCZXCZ
https://www.baidu.com/s?
wd=1&rsv_spt=1&rsv_iqid=Oxa15b13e500003d91&issp=1&f=8&rsv_bp=1&rsv_idx=2&ie=utf-88tn=bai
duhome_pg&rsv_enter=0&rsv_dl=tb&rsv_sug3=2&rsv_sug1=1&rsv_sug7=100&rsv_btype=i&inputT=11698rsv_sug4=16ol7

http协议的默认端口是80
https协议的默认端口是443

2.2.2 请求的方法

HTTP1.0版本只有get和post,在1.1版本后才多出了新的请求方法。
GET:在行业内默认规则下,get请求的所有参数都在URL后明文显示。主要用于查询和展示的相关功能
POST:在行业内默认规则下,请求的参数都在请求体中,不会明文显示。主要用于提交和更新的相关功能
请求方法的区分通过接口文档。

2.2.3 接口的交互内容包含请求与响应

头: header
用于定于数据传输的规范
身体: body
用于传递数据内容

2.2.4 http stutas code

2xx表示请求成功
3xx表示资源发生转义(重定向)
4xx表示请求的接口未找到(404)
5xx表示服务器内部错误(接口有BUG,请求数据不正确)
不是出现200就一定正确。

2.3 项目实战

在接口测试端─般来说会应用到:
1.工具: Jmeter、Postman
2.编程:Python+Requests

企业级接口测试体系:
1.结构设计:
1.逻辑层
2.测试层
3.数据层
2.代码的封装设计:

Java自动化和Python自动化是业内主流两种自动化体系。业内最核心的测试编程技术是python。
市场绝大多数的自动化测试技术都是基于Python来实现的。自动化功能测试只是黑盒测试中高级的测试行为和技术。UI自动化,接口自动化都是黑盒领域下的技术。

python+UnitTest、Python+PyTest

3.3.1 实战框架

python+requests接口测试_第2张图片

一)第一种
api.py

import requests

"""
    接口的关键字驱动封装,是目前业内最主流的接口框架的设计模式
    将常用的请求方法进行函数的封装,以便于后续的可复用和易于维护的性质
"""

class ApiDemo:
    #post
    def do_post(self,url,params=None,headers=None,**kwargs):
        return requests.get(url=url, params=params, headers=headers, **kwargs)
    #get
    def do_get(self,url,params=None,headers=None,**kwargs):
        return requests.get(url=url,params=params,headers=headers,**kwargs)

test_case.py

from api_key.api import ApiDemo
ad = ApiDemo()
#请求数据的准备
url = "http://127.0.0.1:5000/api/login"
data = {
    'username':'admin',
    'password':'123456'
}

#模拟请求下发,并接收响应
#res = requests.post(url=url,json=data)
res = ad.do_post(url=url,json=data)

#解析响应结果,判断本次接口的请求是否成功
print(res)
print(res.text)

#断言
msg = 'success'
assert msg == res.json()['msg'],'断言失败'

二)第二种
test_case.py

import unittest
from api_key.api import ApiDemo
class CaseDemo(unittest.TestCase):
    def test_01_login(self):
        ad = ApiDemo()
        # 请求数据的准备
        url = "http://127.0.0.1:5000/api/login"
        data = {
            'username': 'admin',
            'password': '123456'
        }
        # 模拟请求下发,并接收响应
        # res = requests.post(url=url,json=data)
        res = ad.do_post(url=url, json=data)
        # 解析响应结果,判断本次接口的请求是否成功
        print(res)
        print(res.text)
        # 断言
        msg = 'success'
        self.assertEqual(msg, res.json()['msg'], '断言失败')
    def test_02_login(self):
        ad = ApiDemo()
        # 请求数据的准备
        url = "http://127.0.0.1:5000/api/login"
        data = {
            'username': 'admin2',
            'password': '1234562'
        }
        # 模拟请求下发,并接收响应
        # res = requests.post(url=url,json=data)
        res = ad.do_post(url=url, json=data)
        # 解析响应结果,判断本次接口的请求是否成功
        print(res)
        print(res.text)
        # 断言
        msg = 'success'
        self.assertEqual(msg, res.json()['msg'], '断言失败')
if __name__ == "__main__":
    unittest.main()

api.py

import requests

"""
    接口的关键字驱动封装,是目前业内最主流的接口框架的设计模式
    将常用的请求方法进行函数的封装,以便于后续的可复用和易于维护的性质
"""

class ApiDemo:
    #post
    def do_post(self,url,params=None,headers=None,**kwargs):
        return requests.get(url=url, params=params, headers=headers, **kwargs)
    #get
    def do_get(self,url,params=None,headers=None,**kwargs):
        return requests.get(url=url,params=params,headers=headers,**kwargs)

三)第三种
test_case.py

import unittest
from api_key.api import ApiDemo
from ddt import ddt,file_data
@ddt
class CaseDemo(unittest.TestCase):
    @file_data('../data/user.yaml')
    def test_01_login(self,data):
        ad = ApiDemo()
        # 请求数据的准备
        url = "http://127.0.0.1:5000/api/login"
        # 模拟请求下发,并接收响应
        # res = requests.post(url=url,json=data)
        res = ad.do_post(url=url, json=data)
        # 解析响应结果,判断本次接口的请求是否成功
        print(res)
        print(res.text)
        # 断言
        msg = 'success'
        self.assertEqual(msg, res.json()['msg'], '断言失败')
if __name__ == "__main__":
    unittest.main()

user.yaml

-
  data:
    username: admin
    passwd: '1234456'
-
  data:
    username: admin1
    passwd: '123564456'
-
  data:
    username: admin2
    passwd: '12364456'
-
  data:
    username: admin3
    passwd: '12364456'
- data:
    username: admin4
    passwd: '12364456'

第四种)
test_case.py

import unittest
from api_key.api import ApiDemo
from ddt import ddt,file_data
@ddt
class CaseDemo(unittest.TestCase):
    @file_data('../data/user.yaml')
    def test_01_login(self,data,txt):
        ad = ApiDemo()
        url = "http://127.0.0.1:5000/api/login"
        res = ad.do_post(url=url, json=data)
        self.assertEqual(txt, res.json()['msg'], '断言失败')
if __name__ == "__main__":
    unittest.main()

user.yaml

-
  data:
    username: admin
    passwd: '1234456'
  txt: success
-
  data:
    username: admin1
    passwd: '123564456'
  txt: 用户名或密码错误
-
  data:
    username: admin2
    passwd: '12364456'
  txt: 用户名或密码错误
-
  data:
    username: admin3
    passwd: '12364456'
  txt: 用户名或密码错误
- data:
    username: admin4
    passwd: '12364456'
  txt: 用户名或密码错误

第五种)
test_case.py

import unittest
from api_key.api import ApiDemo
from ddt import ddt,file_data
@ddt
class CaseDemo(unittest.TestCase):

    @classmethod
    def setUp(cls) -> None:
        cls.ad = ApiDemo()

    @file_data('../data/user.yaml')
    def test_01_login(self,data,txt):
        url = "http://127.0.0.1:5000/api/login"
        res = self.ad.do_post(url=url, json=data)
        self.assertEqual(txt, res.json()['msg'], '断言失败')

    def test_02(self):
        # ad = ApiDemo()
        self.ad.do_get('******')
        pass
if __name__ == "__main__":
    unittest.main()

3.3.2 天气接口框架

接口地址:https://www.nowapi.com/?app=intf.appkey

postRequest.py

#第一版
测试天气接口
import requests

# 传url
url = 'http://api.k780.com'

# 传参数
data = {
  'app' : 'weather.future',
  'weaId' : '1',
  'appkey' : '63715',
  'sign' : '33e5595b04f25b6b0348666a94d23151',
  'format' : 'json',
}

# 传请求方法 get或者post
response = requests.get(url,data)

# 拉取响应回来的数据 接口关联 token 转json
res = response.json()
print(res)

# 断言
if res['success'] == '1':
    print("成功")
else:
    print('失败')

#第二版
import requests
def get_request(url,data):
    response = requests.get(url,data)
    res = response.json()
    print(res)
    if res['success'] == '1':
        print("成功")
    else:
        print('失败')
    return res

def post_request(url,data):
    response = requests.post(url,data)
    res = response.json()
    if res['success'] == '1':
        print("成功")
    else:
        print('失败')
    return res

test.py

#第二版
import postRequest
url = 'http://api.k780.com'
data = {
  'app' : 'weather.future',
  'weaId' : '1',
  'appkey' : '63715',
  'sign' : '33e5595b04f25b6b0348666a94d23151',
  'format' : 'json',
}
postRequest.get_request(url,data)
postRequest.post_request(url,data)

#第三版 从excel中提取文档并测试
#提取url与data
import excelRead
import postRequest

r_es = excelRead.r_excel('weter.xlsx','Sheet1')
# print(r_es)

for i in r_es:
    uid = i['id']
    url = i['url']
    data = i['data']
    print(uid,url,data)

    print(type(data))
    #数据类型  转化为字典
    data1 = eval(data)
    print(type(data1))

    res_end = postRequest.post_request(url,data1)
    print(res_end)

excelRead.py

#第三版 从excel中提取文档并测试
#提取url与data

import openpyxl

def r_excel(file_name,sheet_name):
    #读取excel
    workbook = openpyxl.load_workbook(file_name)
    #指定数据在sheet1
    sheet = workbook[sheet_name]
    #读取Sheet1中的case_id
    caseid = sheet.cell(row=1,column=1).value
    # print(caseid)
    #获取总共有多少行
    sun_row = sheet.max_row

    list_name = []

    for i in range(2,sun_row+1):
        dict_name = dict(id = sheet.cell(row=i,column=1).value,
        url = sheet.cell(row=i,column=2).value,
        data = sheet.cell(row=i,column=3).value,
        ecpect = sheet.cell(row=i,column=4).value)
        # print(id,url,data,ecpect)
        list_name.append(dict_name)
    # print(list_name)

    return list_name

python+requests接口测试_第3张图片

其他尝试

#https://www.showapi.com/apiGateway/view/9 天气接口文档
import requests
url = 'http://route.showapi.com/9-3'
area_city = input("请输入需要查询的地区:\n")
data_areaCode = {
    'showapi_appid':'******',
    'showapi_sign':'******'
}
data_areaCode['area'] = area_city
response = requests.post(url,data_areaCode)
res = response.json()
list_ar = res["showapi_res_body"]["list"]


for i in list_ar:
    area = i['area']
    if area_city == area:
        areaCode = i['areaCode']
        break
try:
    print(areaCode)
    data_area = {
        'showapi_appid': '*****',
        'showapi_sign': '*****',
        'areaCode': '510108',
        'needMoreDay': '0',
        'needIndex': '0',
        'need3HourForcast': '0',
        'needAlarm': '0',
        'needHourData': ''
    }
    data_area["areaCode"] = areaCode
    url1 = 'https://route.showapi.com/9-2'
    response1 = requests.post(url1, data_area)
    res1 = response1.json()
    if res1['showapi_res_error'] == '':
        print("成功")
    else:
        print('失败')
    print(res1)
except NameError:
    print(area_city + ":该地区无法查询")

你可能感兴趣的:(python,网络协议,网络)