python 接口断言的方法_Python接口测试课程(第三天)-接口安全验证,参数化及断言...

目录

更多学习资料请加添加作者微信:lockingfree获取

第三天: Python接口测试(二)

各种类型接口的测试

GET请求接口

requests.get(url=url, params=params)

表单类型

requests.post(url=url, data=data)

REST类型

requests.post(url=url, headers={"Content-Type": "application/json"}, data=json.dumps(data)

上传文件

requests.post(url=url, files={"file": open("1.jpg", "rb")})

Session依赖

session=requests.session(); session.post(...); session.post()

接口依赖

接口依赖之动态中间值

resp=requests.get(...);token=resp.split("=")[1];resp2=requests.post(....token...)

验签接口

import hashlib

def md5(str):

m = hashlib.md5()

m.update(str.encode('utf8'))

return m.hexdigest() #返回摘要,作为十六进制数据字符串值

def makeSign(params):

if not isinstance(params, dict):

print("参数格式不正确,必须为字典格式")

return None

if 'sign' in params:

params.pop('sign')

sign = ''

for key in sorted(params.keys()):

sign = sign + key + '=' + str(params[key]) + '&'

sign = md5(sign + 'appsecret=' + appsecret)

params['sign'] = sign

return params

data = makeSign(data);resp = requests.post(url=url, headers=headers, data=json.dumps(data))

接口依赖之Mock Server

Mock和单元测试的桩(Stub)类似, 是通过建立一个模拟对象来解决依赖问题的一种方法.

应用场景:

1. 依赖第三方系统接口, 无法调试

2. 所依赖接口尚未具备(前后端分离项目, 前端开发时,后端接口尚未开发完毕)

3. 所依赖接口需要修改或不稳定

4. 依赖接口较多或场景复杂, 所依赖接口不是主要验证目标的

解决方法:

1. 通过Mock.js/RAP/RAP2来动态生成, 模拟接口返回数据

2. 自己使用Flask大家简单的Mock接口

3. 使用Python自带的mock库

...

SOAP接口

pip install suds

from suds.client import Client

ip = '127.0.0.1'

port = '5001'

client = Client("http://%s:%s/?wsdl" % (ip, port))

result = client.service.addUser("张790", "123456")

print(result)

XML-RPC接口

import xmlrpc.client

user = xmlrpc.client.ServerProxy('http://127.0.0.1:5002')

print(user.getAll())

参数化

参数化是用来解决动态参数问题的

数据文件参数化

csv数据文件

优点:以逗号分隔,轻量级

缺点:参数过多不便于区分

import csv

csv_data = csv.reader(open('data/reg.csv'))

config数据文件

优点:方便支持多组数据,支持备注

import configparser

cf=configparser.ConfigParser()

cf.read('data/reg.config', encoding='utf-8')

cf.sections()

cf.options(section)

cf.get(section,option)

json数据文件

优点:REST接口常用数据格式,格式清楚,适用于多参数及含有嵌套参数

缺点:不支持备注,多组数据不清晰

import json

with open('data/reg.json', encoding='utf-8') as f:

json_data = json.loads(f) #json_data为列表或字典

json的序列化和反序列化

需求:python的字典/列表等数据格式为内存对象,需要做存储(持久化)和进行数据交换

序列化: 从内存对象到可存储数据, 方便存储和交换数据

json.dumps: 列表/字典/json->字符串 ```str_data = json.dumps(dict_data)```

json.dump: 列表/字典/json->数据文件 ```json.dump(dict_data, open(data_file, "w"))```

反序列化: 从存储数据到内存对象

json.loads: 字符串->字典/列表```json.loads('{"a":1,"b":2}') #得到字典{"a":1,"b":2}```

json.load: json数据文档->列表/字典```dict_data = json.load(open('data.json'))```

excel数据文件

优点:直观,构造数据方便

缺点:嵌套数据不方便格式化

pip install xlrd

import xlrd

wb=xlrd.open_workbook("data/reg.xlsx")

sh=wb.sheet_by_index(0)

sh=wb.sheet_by_name('Sheet1")

sh.nrows

sh.ncols

sh.cell(x,y).value

xml数据文件

优点:方便自定义多层结构,SOAP,RPC通用格式

from xml.dom.minidom import parse

dom=parse('data/reg.xml')

root=dom.documentElement

user_nodes=root.getElementsByTagName("user")

user_node.getAttribute('title')

user_node.hasAttribute('title')

name_node=user_node.getElementsByTagName('name')[0]

name=name_node.childNodes[0].data

案例1: 自动执行excel用例并将结果回写excel

数据文件: test_user.xlsx

TestCase

Url

Method

DataType

Data

Excepted

Resp.text

Status

test_user_reg_normal

/api/user/reg/

POST

JSON

{"name":"九小1","passwd": "123456"}

resp.json()["code"]=="100000"

test_user_login_normal

/api/user/login/

POST

FORM

{"name":"九小1","passwd": "123456"}

"成功" in resp.text

import xlrd

from xlutils.copy import copy

import json

import requests

import sys

base_url = "http://127.0.0.1:5000"

def run_excel(file_name, save_file="result.xls"):

wb=xlrd.open_workbook(file_name)

sh=wb.sheet_by_index(0)

wb2 = copy(wb)

sh2 = wb2.get_sheet(0)

for i in range(1,sh.nrows):

url = base_url + sh.cell(i,1).value

data = json.loads(sh.cell(i,4).value)

headers = {}

method = sh.cell(i,2).value

data_type = sh.cell(i,3).value

excepted = sh.cell(i,5).value

if data_type.lower() == 'json':

data = json.dumps(data)

headers = {"Content-Type":"application/json"}

if method.lower() == "get":

resp = requests.get(url=url,headers=headers)

else:

resp = requests.post(url=url,headers=headers,data=data)

if eval(excepted):

status = "PASS"

else:

status = "FAIL"

sh2.write(i,6, resp.text)

sh2.write(i,7, status)

wb2.save(save_file)

print("保存成功")

if __name__ == "__main__":

if len(sys.argv)==2:

run_excel(sys.argv[1])

elif len(sys.argv)>2:

run_excel(sys.argv[1],sys.argv[2])

else:

print("调用格式: python run_excel.py 用例文件 输出文件")

保存脚本为run_excel.py, (最好和数据文件test_user.xlsx放在同一目录下), 在脚本所在目录打开命令行,运行

python run_excel.py test_user.xlsx

生成的result.xls预览

TestCase

Url

Method

DataType

Data

Excepted

Resp.text

Status

test_user_reg_normal

/api/user/reg/

POST

JSON

{"name":"九小1","passwd": "123456"}

resp.json()["code"]=="100000"

{"code":"100001","data":{"name":"\u4e5d\u5c0f1","passwod":"e10adc3949ba59abbe56e057f20f883e"},"msg":"\u5931\u8d25\uff0c\u7528\u6237\u5df2\u5b58\u5728"}

FAIL

test_user_login_normal

/api/user/login/

POST

FORM

{"name":"九小1","passwd": "123456"}

"成功" in resp.text

登录成功

PASS

随机数据参数化

import random

随机数

random.random(): 随机0,1

random.randint(0,100): 随机整数

random.randrange(0,100,2): 随机偶数

random.uniform(1,100): 随机浮点数

随机选择

random.choice('abcdefg')

random.choice(['赵','钱','孙','李','周'])

随机姓名的实现:

#随机汉字: chr(random.randint(0x4e00, 0x9fbf))

name=random.choice(['赵','钱','孙','李','周'])+chr(random.randint(0x4e00, 0x9fbf)

随机样本

random.sample('abcdefg', 2)

随机2个字符拼接: ''.join(random.sample('abcdefg',2)

洗牌

random.shuffle([1, 2, 3, 4, 5, 6]): 随机改版列表数据

断言/检查点

断言/检查点是用来自动验证接口返回数据/业务操作的结果是否满足预期

响应断言

正则表达式

元字符

. : 任意字符

\d: 任意数字 - \D: 任意非数字

\w: 任意字符或数字 - \W: 任意非字符及数字

\s: 任意空白字符(包含空格或\n等) - \S: 任意非空白字符

^: 匹配开头

$: 匹配结尾

{n,m}: 匹配n-m个重复

: 匹配重复任意次(包含0次)

: 匹配重复至少一次

? : 匹配0或1次

(): 分组,获取需要的部分数据

| : 或, 匹配多个pattern

\元字符: 取消元字符转义

贪婪匹配及非贪婪匹配

系统函数

re.findall(): re.S,支持跨行

re.match()

re.search()

re.sub()/re.subn()

re.complie()

数据库断言

从数据库读取数据,验证数据库数据是否符合预期

MySQL断言

pip install pymysql

导入pymysql: import pymysql

建立数据库连接:

conn = pymysql.connect(host='',port=3306,db='',user='',passwd='',charset='utf8')

从连接建立操作游标: cur=conn.cursor()

使用游标执行sql命令: cur.execute("select * from user")

获取执行结果:

cur.fetchall(): 获取所有结果

cur.fetchmany(3): 获取多条结果

cur.fetchone(): 获取一条结果

关闭游标及连接(先关游标再关连接):cur.close();conn.close()

PostgreSQL

pip install pyscopg2

import pyscopg2

conn=pyscopg2.connect(host='',port='',dbname='',user='',password='') # 注意是dbname, password

cur=conn.curser()

cur.execute("...")

cur.fetchall()

Oracle

pip install cx_Oracle

...

Mongodb

pip install pymongo

from pymongo import MongoClient

conn = MongoClient('', 27017)

db = conn.mydb

my_set = db.test_set

for i in my_set.find({"name": "张三"}):

print(i)

print(my_set.findone({"name": "张三"}))

Redis断言

pip install redis

import redis

r = redis.Redis(host='192.168.100.198', port=6379, password='!QE%^E2sdf23RGF@ml239', db=0)

print(r.dbsize())

print(r.get('package'))

print(r.hget('event_order_advance_008aea6a62115ec4923829ee09f76a9c18243f5d', 'user'))

服务器断言

pip install paramiko

import paramiko

client = paramiko.SSHClient()

client.set_missing_host_key_policy(paramiko.AutoAddPolicy())

client.connect('192.168.100.241', 22, username='root', password='1234567', timeout=4)

stdin, stdout, stderr = client.exec_command('cat /proc/meminfo')

print(stdout.read())

client.close()

完整用例:

import requests

import pytest

import json

import hashlib

import re

def md5(string):

m = hashlib.md5()

m.update(string.encode('utf8'))

return m.hexdigest()

def makeSign(data):

sign=''

for key in sorted(data.keys()):

sign += key + '=' + str(data[key]) + '&'

sign += 'appsecret=NTA3ZTU2ZWM5ZmVkYTVmMDBkMDM3YTBi'

data['sign'] = md5(sign)

return data

class DB():

def __init__(self):

# 建立连接

self.conn = pymysql.connect(host='localhost',port=3307,user='root',passwd='',db='api',charset='utf8')

# 建立一个游标

self.cur = self.conn.cursor()

def __del__(self):

self.cur.close()

self.conn.close()

def getUserByName(self, name):

self.cur.execute("select * from user where name='%s'" % name)

return self.cur.fetchone()

def checkUser(self, name, passwd):

user = self.getUserByName(name)

if user:

if user[2] == md5(passwd):

return True

else:

return False

else:

return None

class TestUser(): # pytest识别不能用__init__方法

base_url = 'http://127.0.0.1:5000'

db = DB()

def test_login(self):

url = self.base_url + '/api/user/login/'

data = {"name": "张三", "passwd": "123456"}

resp = requests.post(url=url, data=data)

#断言

assert resp.status_code == 200

assert '登录成功' in resp.text

def test_reg(self):

url = self.base_url + '/api/user/reg/'

headers = {"Content-Type": "application/json"}

data = {'name': '张10', 'passwd': '123456'}

resp = requests.post(url=url, headers=headers, data=json.dumps(data))

#断言

assert resp.json()['code'] == '100000'

assert resp.json()['msg'] == '成功'

assert self.db.getUserByName('张10')

def test_uploadImage(self):

url = self.base_url + '/api/user/uploadImage/'

files = {'file': open("复习.txt")}

resp = requests.post(url=url, files=files)

#断言

assert resp.status_code == 200

assert '成功' in resp.text

# todo 服务器断言

def test_getUserList(self):

session = requests.session()

login_url = self.base_url + '/api/user/login/'

login_data = {"name": "张三", "passwd": "123456"}

session.post(url=login_url, data=login_data)

url = self.base_url + '/api/user/getUserList/'

resp = session.get(url=url)

#断言

assert resp.status_code == 200

assert '用户列表' in resp.text

assert re.findall('\w{32}',t2) != []

def test_updateUser(self):

session = requests.session() # 接口依赖的接口需要用session

get_token_url = self.base_url + '/api/user/getToken/'

params = {"appid": '136425'}

token_resp = session.get(url=get_token_url, params=params)

assert re.findall('token=\w{32}$')

token = token_resp.text.split('=')[1]

url = self.base_url + '/api/user/updateUser/?token=' + token

data = {'name': '张三', 'passwd': '234567'}

headers = {"Content-Type": "application/json"}

resp = session.post(url=url, headers=headers, data=json.dumps(data))

#断言

assert resp.status_code == 200

assert resp.json()['code'] == '100000'

assert resp.json()['msg'] == '成功'

assert self.db.checkUser('张三', '234567')

def test_delUser(self):

url = self.base_url + '/api/user/delUser/'

headers = {"Content-Type": "application/json"}

data = {'name': '张10', 'passwd': '123456'}

data = makeSign(data)

resp = requests.post(url=url, headers=headers, data=json.dumps(data))

#断言

assert resp.status_code == 200

assert resp.json()['code'] == '100000'

assert resp.json()['msg'] == '成功'

assert not self.db.getUserByName('张10')

if __name__ == '__main__':

t = TestUser()

# t.test_updateUser()

# t.test_updateUser()

t.test_delUser()

# pytest.main("-q test_user2.py")

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值