热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

python自动化Python接口自动化测试

一、基础准备1.环境搭建工欲善其事必先利其器,废话不多说。我们先开始搭建环境。#创建项目目录mkdirInterfaceTesting#切换到项目目录下c

一、基础准备

1. 环境搭建

工欲善其事必先利其器,废话不多说。我们先开始搭建环境。

# 创建项目目录

mkdir InterfaceTesting

# 切换到项目目录下

cd InterfaceTesting

# 安装虚拟环境创建工具

pip install virtualenv

# 创建虚拟环境,env代表虚拟环境的名称,可自行定义

virtualenv env

# 启动虚拟环境,执行下面命令后会发现路径上有 (env) 字样的标识

source env/Scripts/activate

# 查看 (env) 环境下使用的 Python 和 pip 工具版本

ls env/Scripts/

# *** 安装 requests ***

pip install requests

# 退出虚拟环境,退出后路径上的 (env) 字样的标识消失

cd env/Scripts/

deactivate

# 导出环境所需要的模块的清单

pip freeze >> requirements.txt

# 上传 GitHub 时,将下面项忽略上传

echo env/ >> .gitignore

echo InterfaceTesting.iml >> .gitignore

echo __pycache__/ >> .gitignore

# 将代码传至 GitHub

# 本地仓初始化

git init

# 创建本地仓与 GitHub 仓的远程链接

git remote add github 你的github仓的地址

# 将代码添加到暂存区

git add .

# 将代码提交到

git commit -m "init environment"

# 将代码上传到GitHub仓中

git push github master

初始化环境的项目结构示例如下:

36fe6ac9-b4c2-4fab-b9f3-cd5c67450e88.png!large

2. 接口基础知识

2.1 接口分类

接口一般来说有两种,一种是程序内部的接口,一种是系统对外的接口。

(1) webservice接口:走soap协议通过http传输,请求报文和返回报文都是xml格式的,我们在测试的时候都要通过工具才能进行调用,测试。

(2) http api 接口:走http协议,通过路径来区分调用的方法,请求报文都是key-value形式的,返回报文一般都是json串,有get和post等方法。

2.2 接口请求类型

根据接口的请求方法,常用的几种接口请求方式:

(1) GET:从指定资源获取数据

(2) POST:向指定的资源请求被处理的数据(例如用户登录)

(3) PUT:上传指定的URL,一般是修改,可以理解为数据库中的 update

(4) DELETE:删除指定资源

二、Requests 快速上手

1. requests基础

所有的数据测试目标以一个开源的接口模拟网站【HTTPBIN】为测试对象。

1.1 发送请求 #!/usr/bin/env python

# -*- encoding: utf-8 -*-

"""

@File : requests_send_request.py

@Time : 2019/9/2 11:54

@Author : Crisimple

@Github : https://crisimple.github.io/

@Contact : Crisimple@foxmail.com

@License : (C)Copyright 2017-2019, Micro-Circle

@Desc : None

"""

import requests

# 1.requests请求方式

# (1) GET请求方式

httpbin_get = requests.get('http://httpbin.org/get', data={'key': 'value'})

print('httpbin_get: ', httpbin_get.text)

# (2) POST请求方式

httpbin_post = requests.post('https://httpbin.org/post', data={'key': 'value'})

print('httpbin_post: ', httpbin_post.text)

# (3) PUT请求方式

httpbin_put = requests.put('https://httpbin.org/put', data={'key': 'value'})

print('httpbin_put: ', httpbin_put.text)

# (4) DELETE请求方式

httpbin_delete = requests.delete('https://httpbin.org/delete', data={'key': 'value'})

print('httpbin_delete', httpbin_delete)

# (5) PATCH亲求方式

httpbin_patch = requests.patch('https://httpbin.org/patch', data={'key': 'value'})

print('httpbin_patch', httpbin_patch)

1.2 参数传递

常用的参数传递形式有四种:【

(1)字典形式的参数:payload = {'key1': 'value1', 'key2': 'value2'}

(2) 元组形式的参数:payload = (('key1', 'value1'), ('key2', 'value2'))

(3) 字符串形式的参数:payload = {'string1', 'value1'}

(4) 多部份编码的文件:files = {

# 显示设置文件名、文件类型和请求头

'file': ('report.xls', open('report.xls', 'rb'), 'application/vnd.ms-excel', {'Expires': '0'})

}

#!/usr/bin/env python

# -*- encoding: utf-8 -*-

"""

@File : requests_transfer_parameter.py

@Time : 2019/9/2 12:39

@Author : Crisimple

@Github : https://crisimple.github.io/

@Contact : Crisimple@foxmail.com

@License : (C)Copyright 2017-2019, Micro-Circle

@Desc : 参数传递:字典、元组、字符串、文件

"""

import requests

# 2. 参数传递

# (1) 传参参数为字典形式: 数据字典会在发送请求时会自动编码为表单形式

def transfer_dict_parameter():

payload = {

'key1': 'value1',

'key2': 'value2'

}

transfer_dict_parameter_result = requests.post('https://httpbin.org/post', params=payload)

print("transfer_dict_parameter_url: ", transfer_dict_parameter_result.url)

print("transfer_dict_parameter_text: ", transfer_dict_parameter_result.text)

transfer_dict_parameter()

# (2) 传参参数为元组形式: 应用于在表单中多个元素使用同一 key 的时候

def transfer_tuple_parameter():

payload = (

('key1', 'value1'),

('key1', 'value2')

)

transfer_tuple_parameter_result = requests.post('https://httpbin.org/post', params=payload)

print('transfer_tuple_parameter_url: ', transfer_tuple_parameter_result.url)

print('transfer_tuple_parameter_text: ', transfer_tuple_parameter_result.text)

transfer_tuple_parameter()

# (3) 传参参数形式是字符串形式

def transfer_string_parameter():

payload = {

'string1': 'value'

}

transfer_string_parameter_result = requests.post('https://httpbin.org/post', params=payload)

print('transfer_string_parameter_url: ', transfer_string_parameter_result.url)

print('transfer_string_parameter_text: ', transfer_string_parameter_result.text)

transfer_string_parameter()

# (4) 传参参数形式:一个多部分编码(Multipart-Encoded)的文件

def transfer_multipart_encoded_file():

interface_url = 'https://httpbin.org/post'

files = {

# 显示设置文件名、文件类型和请求头

'file': ('report.xls', open('report.xls', 'rb'), 'application/vnd.ms-excel', {'Expires': '0'})

}

transfer_multipart_encoded_file_result = requests.post(url=interface_url, files=files)

print('transfer_multipart_encoded_file_result_url: ', transfer_multipart_encoded_file_result.url)

print('transfer_multipart_encoded_file_result_url: ', transfer_multipart_encoded_file_result.text)

transfer_multipart_encoded_file()

1.3 接口响应

给接口传递参数,请求接口后,接口会给我们我们响应返回,接口在返回的时候,会给我们返回一个状态码来标识当前接口的状态。

(1)状态码

【1xx | ---- |信息类的状态码 **

| 100 | Continue | 服务器仅接收到部分请求,但是一旦服务器并没有拒绝该请求,客户端应该继续发送其余的请求。

| 101 | Switching Protocols | 服务器转换协议,服务器将遵从客户的请求转换到另外一种协议

**2xx | ---- |*成功类的状态码 *

| 200 | OK | 请求成功(是对 GET 或 POST 的请求应答文档)

| 201 | Created | 请求被创建完成,同时信的资源被创建

| 202 | Accepted | 供处理的请求已被接收,但是处理未完成

| 203 | Non-authoritative Information | 文档已正常地返回,但一些应答头可能不正确,以为使用的式文档的拷贝

| 204 | No Content | 没有新文档。浏览器应该继续显示原来的文档。如果用户定期地刷新页面,而Servlet可以确定用户文档足够新,这个状态代码是很有用的。

| 205 | Reset Content | 没有新文档。但浏览器应该重置它所显示的内容。用来强制浏览器清除表单输入内容。

| 206 | Partial Content | 客户发送了一个带有Range头的GET请求,服务器完成了它。

#!/usr/bin/env python

# -*- encoding: utf-8 -*-

"""

@File : response_code.py

@Time : 2019/9/2 15:41

@Author : Crisimple

@Github : https://crisimple.github.io/

@Contact : Crisimple@foxmail.com

@License : (C)Copyright 2017-2019, Micro-Circle

@Desc : None

"""

import requests

# 1. 返回接口状态码:200

def response_200_code():

interface_200_url = 'https://httpbin.org/status/200'

response_get = requests.get(interface_200_url)

response_get_code = response_get.status_code

print('response_get_code: ', response_get_code)

response_200_code()

# 2.返回接口状态码:400

def response_400_code():

interface_400_url = 'https://httpbin.org/status/400'

response_get = requests.get(interface_400_url)

response_get_code = response_get.status_code

print('response_get_code: ', response_get_code)

response_400_code()

(2)响应头 #!/usr/bin/env python

# -*- encoding: utf-8 -*-

"""

@File : response_content.py

@Time : 2019/9/2 15:41

@Author : Crisimple

@Github : https://crisimple.github.io/

@Contact : Crisimple@foxmail.com

@License : (C)Copyright 2017-2019, Micro-Circle

@Desc : None

"""

import requests

# 1. 返回接口状态码:

# (1). 返回接口状态码:200

def response_200_code():

interface_200_url = 'https://httpbin.org/status/200'

response_get = requests.get(interface_200_url)

response_get_code = response_get.status_code

print('response_get_code: ', response_get_code)

response_200_code()

# (2).返回接口状态码:400

def response_400_code():

interface_400_url = 'https://httpbin.org/status/400'

response_get = requests.get(interface_400_url)

response_get_code = response_get.status_code

print('response_get_code: ', response_get_code)

response_400_code()

# (3) 重定向接口返回状态码:301

def response_301_code():

interface_url = 'https://butian.360.cn'

response_get = requests.get(interface_url)

response_get_code = response_get.status_code

print('response_get_code: ', response_get_code)

response_301_code()

# ------------------------------------------------------

# 2. 响应内容

  响应内容的请求头、查看文本、编码方式、二进制响应、原始响应。

def response_contents():

url = 'https://httpbin.org/get'

response_get = requests.get(url=url)

# 响应头

print('response_get_headers', response_get.headers)

# 响应文本

print('response_get_text: ', response_get.text)

# 文本编码方式

print('response_get_encoding: ', response_get.encoding)

# 二进制响应内容

print('response_get_content: ', response_get.content)

# 原始响应内容

origin_content = response_get.raw

origin_content_read = origin_content.read(10)

print('origin_content: ', origin_content)

print('origin_content_read: ', origin_content_read)

response_contents()

1.4 接口其他处理

(1) 操作COOKIEs import requests

import time

url = 'https://httpbin.org/get'

def operator_COOKIEs():

r = requests.get(url)

print('r.COOKIEs: ', r.COOKIEs)

jar = requests.COOKIEs.RequestsCOOKIEJar()

jar.set('tasty_COOKIE', 'yum', domain='httpbin.org', path='/COOKIEs')

jar.set('gross_COOKIE', 'blech', domain='httpbin.org', path='/elsewhere')

r2 = requests.get(url=url, COOKIEs=jar)

print('r2.text', r2.text)

operator_COOKIEs()

(2) 请求历史 import requests

url = 'https://httpbin.org/get'

def request_history():

r = requests.get(url=url)

print('r.history: ', r.history)

request_history()

(3) 超时请求

requests 在经过 timeout 参数设定的秒数时间之后停止等待响应。

import requests

import time

def timeout():

print(time.time())

url = 'https://httpbin.org/get'

print(time.time())

r = requests.get(url, timeout=5)

print(time.time())

timeout()

(4) 错误与异常

常见的错误异常有:

· 遇到网络问题(如:DNS 查询失败、拒绝连接等时),requests 会抛出一个 ConnectionError 异常。

· 如果 HTTP 请求返回了不成功的状态码, Response.raise_for_status() 会抛出一个 HTTPError异常。

· 若请求超时,则超出一个 Timeout 异常。

· 若请求超过了设定的最大重定向次数,则会抛出一个 TooManyRedirects 异常。

· 所有 Requests 显式抛出的异常都继承自 requests.exceptions.RequestsException。

2.1 会话对象

2.2 请求与响应对象

2.3 准备的请求

2.4 SSL证书验证

2.5 客户端证书

三、接口测试实战

1. 百度翻译接口测试

理论千千万万,实战才是真理。百度翻译提供了一套成熟的翻译接口(不是恰饭�),我们就用此接口对前面理论进行实战。【

#!/usr/bin/env python

# -*- encoding: utf-8 -*-

"""

@File : baidu_translate.py

@Time : 2019/9/2 20:05

@Author : Crisimple

@Github : https://crisimple.github.io/

@Contact : Crisimple@foxmail.com

@License : (C)Copyright 2017-2019, Micro-Circle

@Desc : None

"""

import requests

import random

import hashlib

import urllib

import json

class BaiduTranslate(object):

def __init__(self, word):

# 你要翻译的元素

self.q = word

self.fromLang = 'en'

self.toLang = 'zh'

self.baidu_translate = 'https://api.fanyi.baidu.com'

self.translate_api_url = '/api/trans/vip/translate'

# 百度开发者配置信息

self.appid = 'XXXXXXXX'

self.secretKey = 'XXXXXXXX'

# 开发配置

self.salt = random.randint(32768, 65536)

self.sign = self.appid + self.q + str(self.salt) + self.secretKey

m1 = hashlib.md5()

m1.update(self.sign.encode('utf-8'))

self.sign = m1.hexdigest()

self.my_url = self.translate_api_url + '?appid=' + self.appid + '&q=' + urllib.request.quote(self.q) + '&from=' + self.fromLang + '&to=' + self.toLang + '&salt=' + str(self.salt) + '&sign=' + self.sign

def en_translate_zh(self):

re = requests.request('post', self.baidu_translate + self.my_url)

print(' re.text', re.text)

re_json = json.loads(re.text)

print(' re_json', re_json)

if __name__ == "__main__":

bt = BaiduTranslate('test')

bt.en_translate_zh()

2. urllib请求接口

有了requests库请求接口了,为什么要再用urllib来请求接口呢?因为urllib是python的基础库,不需要下载安装,在对环境要求甚高的环境下,在不破坏原来的环境下,依然可以让自动化代码依然运行。【

#!/usr/bin/env python

# -*- encoding: utf-8 -*-

"""

@File : urllib_request.py

@Time : 2019/9/2 20:49

@Author : Crisimple

@Github : https://crisimple.github.io/

@Contact : Crisimple@foxmail.com

@License : (C)Copyright 2017-2019, Micro-Circle

@Desc : None

"""

from urllib import request

from urllib import parse

def urllib_request():

base_url = 'http://www.tuling123.com/openapi/api'

payload = {

'key1': 'Your',

'key2': '你好'

}

ur = request.Request(url=base_url)

ur_response = request.urlopen(ur)

print(' ur_response: ', ur_response)

print(' ur_response_getcode: ', ur_response.getcode)

print(' ur_response_headers: ', ur_response.headers)

data = parse.urlencode(payload).encode('utf-8')

url_payload = request.Request(url=base_url, data=data)

url_payload_response = request.urlopen(url_payload)

print(' url_payload_response: ', url_payload_response)

print(' url_payload_response_getcode: ', url_payload_response.getcode)

print(' url_payload_response_headers: ', url_payload_response.headers)

print(' url_payload_response_msg: ', url_payload_response.msg)

print(' url_payload_response_read: ', url_payload_response.read)

urllib_request()

四、搭建测试接口平台

自搭建的接口平台使用Django框架进行开发,基于当前接口的需求(接口的增、删、改、查)功能,搭建一个满足需要的接口测试平台。

1. 环境搭建

1.1 项目创建

# 下载 django 框架库

pip install django

# 创建 django 工程

django-admin startproject InterfaceTestingMock

# 创建 api_crud app

cd InterfaceTestingMock

python manage.py startapp interface_crud

# 创建 api_mock 工程的虚拟运行环境

viutualenv env

# 激活虚拟环境

source env/Scripts/activate

# 退出虚拟环境

deactivate

# 导出虚拟环境 env 所需要的库

pip freeze >> requirements.txt

1.2 接口开发配置

(1) 创建表结构

python manage.py migrate

(2) 编写模型层代码,以下语句相当于创建了两张表:User,Article

# interface_crud.models.py

from django.db import models

# Create your models here.

class User(models.Model):

id = models.AutoField(primary_key=True)

user_name = models.CharField(max_length=50)

user_password = models.CharField(max_length=100)

# active inactive

status = models.CharField(max_length=10)

class Article(models.Model):

id = models.AutoField(primary_key=True)

title = models.CharField(max_length=50)

content = models.TextField()

# delete alive

status = models.CharField(max_length=10)

(3) 新增表,执行下面语句让 django 知道表发生了变化

python manage.py makemigrations interface_crud

(4) 再次创建表

python manage.py migrate

(5) 生成创建超级管理员账号

# 依次数据用户名、邮箱地址、密码、重复密码、确认(y)

python manage.py createsuperuser

(6) 配置接口请求地址

# InterfaceTestingMock.urls.py

from django.contrib import admin

from django.urls import path

from interface_crud.views import add_article, modify_article

urlpatterns = [

path('admin/', admin.site.urls),

path('articles/', add_article),

path('articles', modify_article)

]

## 2. 接口开发

&emsp:&emsp:就目前常用的接口参数传参形式分别有:表单类接口传参,多用于提供给前端页面(后续学习跟进总结);另一种常用的就是 json 传参形式的,这种传参形式能够满足开发处业务逻辑更为复杂的接口,本次接口开发就采用该形式。【**[GitHub示例](https://github.com/Crisimple/InterfaceTesting/commit/608019d5402b08673fa31cc4bd762a96d96d7063)**】---【**[GitHub示例](https://github.com/Crisimple/InterfaceTesting/commit/0fc0ddcd965081686b53b657d63a26f2d06cb6c9)**】

```!

备注:2.1-2.6是根据【** [秦无殇的博客](https://www.cnblogs.com/webDepOfQWS/p/10693152.html)**】学习整理而来,谢谢这位老哥❀

2.1 查询文章接口

9576d3fc-3962-4c94-afeb-ec2b91c449e6.png!large

from interface_crud.models import Article

from django.http import JsonResponse, HttpResponse

import json

# Create your views here.

# 查询文章

def query_article(request):

if request.method == 'GET':

articles = {}

query_articles = Article.objects.all()

print('query_articles: ', query_articles)

for title in query_articles:

articles[title.title] = title.status

return JsonResponse({"status": "BS.200", "all_titles": articles, "msg": "query articles success."})

print("request.body", request.body)

else:

return HttpResponse("方法错误")

2.2 增加文章接口

17a8eb0d-1142-4902-904f-0449bd893d85.png!large

# 增加文章

def add_article(request):

auth_res = user_auth(request)

if auth_res == "auth_fail":

return JsonResponse({"status": "BS.401", "msg": "user auth failed."})

else:

if request.method == "POST":

# b''

print('request.body: ', request.body)

print('request.body: ', type(request.body))

req_dict = json.loads(request.body)

print('req_json: ', req_dict)

print('req_json: ', type(req_dict))

key_flag = req_dict.get('title') and req_dict.get('content') and len(req_dict) == 2

print('key_flag: ', key_flag)

# 判断请求体是否正确

if key_flag:

title = req_dict['title']

content = req_dict['content']

# title返回的是一个list

title_exist = Article.objects.filter(title=title)

# 判断是否存在同名的title

if len(title_exist) != 0:

return JsonResponse({"status": "BS.400", "msg": "title already exist, fail to publish."})

"""

插入数据

"""

add_art = Article(title=title, content=content, status='alive')

add_art.save()

return HttpResponse(add_art)

return JsonResponse({"status": "BS.200", "msg": "add article success."})

else:

return JsonResponse({"status": "BS.400", "message": "please check param."})

else:

return HttpResponse("方法错误,你应该使用POST请求方式")

2.3 修改文章接口 # 更新文章

def modify_article(request, article_id):

auth_res = user_auth(request)

if auth_res == "auth_fail":

return JsonResponse({"status": "BS.401", "msg": "user auth failed."})

else:

if request.method == 'POST':

modify_req = json.loads(request.body)

try:

article = Article.objects.get(id=article_id)

print("article", article)

key_flag = modify_req.get('title') and modify_req.get('content') and len(modify_req) == 2

if key_flag:

title = modify_req['title']

content = modify_req['content']

title_exist = Article.objects.filter(title=title)

if len(title_exist) > 1:

return JsonResponse({"status": "BS.400", "msg": "title already exist."})

# 更新文章

old_article = Article.objects.get(id=article_id)

old_article.title = title

old_article.content = content

old_article.save()

return JsonResponse({"status": "BS.200", "msg": "modify article sucess."})

except Article.DoesNotExist:

return JsonResponse({"status": "BS.300", "msg": "article is not exists,fail to modify."})

else:

return HttpResponse("方法错误,你应该使用POST请求方式")

2.4 删除文章接口 # 删除文章

def delete_article(request, article_id):

auth_res = user_auth(request)

if auth_res == "auth_fail":

return JsonResponse({"status": "BS.401", "msg": "user auth failed."})

else:

if request.method == 'DELETE':

try:

article = Article.objects.get(id=article_id)

article_id = article.id

article.delete()

return JsonResponse({"status": "BS.200", "msg": "delete article success."})

except Article.DoesNotExist:

return JsonResponse({"status": "BS.300", "msg": "article is not exists,fail to delete."})

else:

return HttpResponse("方法错误,你应该使用DELETE请求方式")

2.5 token认证 # 用户认证

# 四个简单的接口已经可以运行了,但是在发请求之前没有进行鉴权,毫无安全性可言。下面来实现简单的认证机制。需要用到内建模块hashlib,hashlib提供了常见的摘要算法,如MD5,SHA1等。

def user_auth(request):

token = request.META.get("HTTP_X_TOKEN", b'')

print("token: ", token)

if token:

# 暂时写上 auth 接口返回的数据

if token == '0a6db4e59c7fff2b2b94a297e2e5632e':

return "auth_success"

else:

return "auth_fail"

else:

return "auth_fail"

2.6 接口测试

在接口开发是不断开发不断测试是一个非常好的习惯。

#!/usr/bin/env python

# -*- encoding: utf-8 -*-

"""

@File : interface_crud_tests.py

@Time : 2019/9/4 14:22

@Author : Crisimple

@Github : https://crisimple.github.io/

@Contact : Crisimple@foxmail.com

@License : (C)Copyright 2017-2019, Micro-Circle

@Desc : None

"""

import requests

import unittest

class TestInterfaceCrud(unittest.TestCase):

@unittest.skip("跳过 test_query_article 测试")

def test_query_article(self):

payload = {}

res = requests.get('http://127.0.0.1:8000/query_article/', params=payload)

print("test_query_article: ", res.text)

@unittest.skip("跳过 test_add_article 测试")

def test_add_article(self):

payload = {

"title": "title5",

"content": "content5",

}

Headers = {

# "Authorization": '通用的token,但是该接口使用的是X-Token',

"Content-Type": "application/json; charset=utf-8",

"Accept": "application/json",

"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Safari/537.36 Core/1.70.3730.400 QQBrowser/10.5.3805.400",

"X-Token": "0a6db4e59c7fff2b2b94a297e2e5632e"

}

res = requests.post('http://127.0.0.1:8000/add_article/', headers=Headers, json=payload)

print(res.request)

print(res.text)

@unittest.skip("跳过 test_modify_article 测试")

def test_modify_article(self):

payload = {

"title": "title1",

"content": "content1",

}

Headers = {

# "Authorization": '通用的token,但是该接口使用的是X-Token',

"Content-Type": "application/json; charset=utf-8",

"Accept": "application/json",

"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Safari/537.36 Core/1.70.3730.400 QQBrowser/10.5.3805.400",

"X-Token": "0a6db4e59c7fff2b2b94a297e2e5632e"

}

res = requests.post('http://127.0.0.1:8000/modify_article/1', headers=Headers, json=payload)

print(res.request)

print(res.text)

# @unittest.skip("跳过 test_delete_article 测试")

def test_delete_article(self):

payload = {

"title": "title2",

"content": "content2",

}

Headers = {

# "Authorization": '通用的token,但是该接口使用的是X-Token',

"Content-Type": "application/json; charset=utf-8",

"Accept": "application/json",

"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Safari/537.36 Core/1.70.3730.400 QQBrowser/10.5.3805.400",

"X-Token": "0a6db4e59c7fff2b2b94a297e2e5632e"

}

res = requests.delete('http://127.0.0.1:8000/delete_article/2', headers=Headers, json=payload)

print(res.request)

print(res.text)

@unittest.skip("跳过 test_test_api 测试")

def test_test_api(self):

payload = {

'title': 'title1',

'content': 'content1',

'status': 'alive'

}

res = requests.post('http://127.0.0.1:8000/test_api/')

print(res.text)

if __name__ == '__main__':

unittest.main()

五、接口自动化

1. 数据处理

1.1 Excel中数据

获取 excel 的第几 sheet 页,行数,列数,单元格值,数据写入 excel操作。【

#!/usr/bin/env python

# -*- encoding: utf-8 -*-

"""

@File : operate_excel.py

@Time : 2019/9/5 10:07

@Author : Crisimple

@Github : https://crisimple.github.io/

@Contact : Crisimple@foxmail.com

@License : (C)Copyright 2017-2019, Micro-Circle

@Desc : 对 Excel 的读写操作

"""

import xlrd

from xlutils.copy import copy

class OperateExcel(object):

def __init__(self, file_name=None, sheet_id=None):

"""

:param file_name: excel文件的具体路径名称

:param sheet_id: 要操作的第几 sheet 页

"""

if file_name:

self.file_name = file_name

else:

self.file_name = '../data/util_data/operate_excel.xls'

if sheet_id:

self.sheet_id = sheet_id

else:

self.sheet_id = 0

self.sheet_table = self.get_sheet()

# 获取 sheet 页操作对象

def get_sheet(self):

data = xlrd.open_workbook(self.file_name)

sheet_table = data.sheets()[self.sheet_id]

return sheet_table

# 获取该 sheet 页的行数和列数,拿到的是一个元组

def get_sheet_nrows_ncols(self):

return self.sheet_table.nrows, self.sheet_table.ncols

# 获取该 sheet 页的行数

def get_sheet_nrows(self):

return self.sheet_table.nrows

# 获取该 sheet 页的列数

def get_sheet_ncols(self):

return self.sheet_table.ncols

# 获取具体单元格的数据

def get_sheet_cell(self, row, col):

"""

:param row: 单元格的行值

:param col: 单元格的列值

:return: cell_data

"""

cell_data = self.sheet_table.cell_value(row, col)

return cell_data

# 写入数据到 excel 中

def write_to_excel(self, row, col, value):

# 同样的先打开 excel 操作句柄

data = xlrd.open_workbook(self.file_name)

copy_data = copy(data)

# 选择写入的 sheet 页

copy_data_sheet = copy_data.get_sheet(0)

# 写入数据

copy_data_sheet.write(row, col, value)

# 保存数据

copy_data.save(self.file_name)

if __name__ == "__main__":

oe = OperateExcel()

print("获取 excel 表的行数和列表,返回元组形式:", oe.get_sheet_nrows_ncols())

print("获取 excel 表的行数:", oe.get_sheet_nrows())

print("获取 excel 表的列数:", oe.get_sheet_ncols())

print("获取单元格(1, 1)的值:", oe.get_sheet_cell(1, 1))

print("获取单元格(1, 2)的值:", oe.get_sheet_cell(1, 2))

print("获取单元格(2, 2)的值:", oe.get_sheet_cell(2, 2))

oe.write_to_excel(17, 7, '写入的数据')

1.2 JSON中数据

#!/usr/bin/env python

# -*- encoding: utf-8 -*-

"""

@File : operate_json.py

@Time : 2019/9/5 12:24

@Author : Crisimple

@Github : https://crisimple.github.io/

@Contact : Crisimple@foxmail.com

@License : (C)Copyright 2017-2019, Micro-Circle

@Desc : 操作 JSON 文件中的数据

"""

import json

class OperateJson(object):

def __init__(self, file_name=None):

if file_name:

self.file_name = file_name

else:

self.file_name = '../data/util_data/operate_json.json'

self.data = self.get_json()

# 读取 json 文件

def get_json(self):

with open(self.file_name, encoding='utf-8') as fp:

data = json.load(fp)

return data

# 根据关键词读取数据

def get_key_data(self, key):

return self.data[key]

if __name__ == '__main__':

oj = OperateJson()

print('login: ', oj.get_key_data("login"))

print('login.username: ', oj.get_key_data("login")["username"])

print('login.password: ', oj.get_key_data("login")["username"])

print('logout: ', oj.get_key_data("logout"))

print('logout.code: ', oj.get_key_data("logout")["code"])

print('logout.info: ', oj.get_key_data("logout")["info"]) {

"login": {

"username": "kevin",

"password": "121345"

},

"logout": {

"code": 200,

"info": "logout"

}

}

1.3 数据库中的数据

数据库用的常用的MySQL。【

远程连接数据库可能会连接出错的解决方法:GRANT ALL PRIVILEGES ON . TO 'root'@'%' IDENTIFIED BY '你的密码' WITH GRANT OPTION;

#!/usr/bin/env python

# -*- encoding: utf-8 -*-

"""

@File : operate_mysql.py

@Time : 2019/9/5 16:10

@Author : Crisimple

@Github : https://crisimple.github.io/

@Contact : Crisimple@foxmail.com

@License : (C)Copyright 2017-2019, Micro-Circle

@Desc : 操作 数据库 中的数据

"""

import pymysql

import json

class OperateMysql(object):

def __init__(self):

# 数据库初始化连接

self.connect_interface_testing = pymysql.connect(

"XXX.XXX.XXX.XXX",

"XXX",

"XXXXXXXX",

"InterfaceTesting",

cursorclass=pymysql.cursors.DictCursor

)

# 创建游标操作数据库

self.cursor_interface_testing = self.connect_interface_testing.cursor()

def select_data(self, sql):

# 执行 sql 语句

self.cursor_interface_testing.execute(sql)

# 获取查询到的第一条数据

first_data = self.cursor_interface_testing.fetchone()

# 将返回结果转换成 str 数据格式

first_data = json.dumps(first_data)

return first_data

if __name__ == "__main__":

om = OperateMysql()

res = om.select_data(

"""

SELECT * FROM test_table;

"""

)

print(res)

2. 邮件告警

通常我们做接口自动化测试的时候,自动化用例执行结束后,我们需要首先需要看自动化用例是不是执行结束了,另外它的执行结果是什么。我们不可能一直紧盯着脚本执行,所以当自动化执行结束后,我们需要发送邮件来进行提醒并把自动化的执行情况邮件通知。

#!/usr/bin/env python

# -*- encoding: utf-8 -*-

"""

@File : email_config.py

@Time : 2019/9/5 18:58

@Author : Crisimple

@Github : https://crisimple.github.io/

@Contact : Crisimple@foxmail.com

@License : (C)Copyright 2017-2019, Micro-Circle

@Desc : 发送邮件配置

"""

import smtplib

from email.mime.text import MIMEText

class EmailConfig(object):

global send_user

global mail_host

global password

send_user = 'xxxxxxx@163.com'

mail_host = 'smtp.163.com'

password = 'xxxxxxxx'

def send_config(self, user_lists, subject, content):

user &#61; "发件人昵称" &#43; "<" &#43; send_user &#43; ">"

message &#61; MIMEText(content, _subtype&#61;"plain", _charset&#61;"utf-8")

message[&#39;Subject&#39;] &#61; subject

message[&#39;From&#39;] &#61; user

message[&#39;To&#39;] &#61; ";".join(user_lists)

server &#61; smtplib.SMTP()

server.connect(mail_host)

server.login(send_user, password)

server.sendmail(user, user_lists, message.as_string())

server.close()

def send_mail(self, pass_cases, fail_cases, not_execute_cases):

pass_num &#61; float(len(pass_cases))

fail_num &#61; float(len(fail_cases))

not_execute_num &#61; float(len(not_execute_cases))

execute_num &#61; float(pass_num &#43; fail_num)

total_cases &#61; float(pass_num &#43; fail_num &#43; not_execute_num)

pass_ratio &#61; "%.2f%%" % (pass_num / total_cases * 100)

fail_ratio &#61; "%.2f%%" % (fail_num / total_


推荐阅读
  • Python 程序转换为 EXE 文件:详细解析 .py 脚本打包成独立可执行文件的方法与技巧
    在开发了几个简单的爬虫 Python 程序后,我决定将其封装成独立的可执行文件以便于分发和使用。为了实现这一目标,首先需要解决的是如何将 Python 脚本转换为 EXE 文件。在这个过程中,我选择了 Qt 作为 GUI 框架,因为之前对此并不熟悉,希望通过这个项目进一步学习和掌握 Qt 的基本用法。本文将详细介绍从 .py 脚本到 EXE 文件的整个过程,包括所需工具、具体步骤以及常见问题的解决方案。 ... [详细]
  • 为了确保iOS应用能够安全地访问网站数据,本文介绍了如何在Nginx服务器上轻松配置CertBot以实现SSL证书的自动化管理。通过这一过程,可以确保应用始终使用HTTPS协议,从而提升数据传输的安全性和可靠性。文章详细阐述了配置步骤和常见问题的解决方法,帮助读者快速上手并成功部署SSL证书。 ... [详细]
  • 在Cisco IOS XR系统中,存在提供服务的服务器和使用这些服务的客户端。本文深入探讨了进程与线程状态转换机制,分析了其在系统性能优化中的关键作用,并提出了改进措施,以提高系统的响应速度和资源利用率。通过详细研究状态转换的各个环节,本文为开发人员和系统管理员提供了实用的指导,旨在提升整体系统效率和稳定性。 ... [详细]
  • 使用 ListView 浏览安卓系统中的回收站文件 ... [详细]
  • 优化后的标题:深入探讨网关安全:将微服务升级为OAuth2资源服务器的最佳实践
    本文深入探讨了如何将微服务升级为OAuth2资源服务器,以订单服务为例,详细介绍了在POM文件中添加 `spring-cloud-starter-oauth2` 依赖,并配置Spring Security以实现对微服务的保护。通过这一过程,不仅增强了系统的安全性,还提高了资源访问的可控性和灵活性。文章还讨论了最佳实践,包括如何配置OAuth2客户端和资源服务器,以及如何处理常见的安全问题和错误。 ... [详细]
  • 深入探索HTTP协议的学习与实践
    在初次访问某个网站时,由于本地没有缓存,服务器会返回一个200状态码的响应,并在响应头中设置Etag和Last-Modified等缓存控制字段。这些字段用于后续请求时验证资源是否已更新,从而提高页面加载速度和减少带宽消耗。本文将深入探讨HTTP缓存机制及其在实际应用中的优化策略,帮助读者更好地理解和运用HTTP协议。 ... [详细]
  • 基于Linux系统的Kickstart自动化服务器部署方案
    本文针对企业需求,提出了一种基于Linux系统的Kickstart自动化服务器部署方案。该方案旨在通过无盘批量安装操作系统,提高企业IT基础设施的部署效率。Kickstart是一种利用Anaconda工具实现服务器自动化安装的技术,能够显著简化和加速操作系统的安装过程。通过详细的实施规划,本文介绍了Kickstart的工作原理及其在实际部署中的应用,为企业提供了高效的自动化部署解决方案。 ... [详细]
  • 本指南介绍了 `requests` 库的基本使用方法,详细解释了其七个主要函数。其中,`requests.request()` 是构建请求的基础方法,支持其他高级功能的实现。此外,我们还重点介绍了如何使用 `requests.get()` 方法来获取 HTML 网页内容,这是进行网页数据抓取和解析的重要步骤。通过这些基础方法,读者可以轻松上手并掌握网页数据抓取的核心技巧。 ... [详细]
  • 基于Net Core 3.0与Web API的前后端分离开发:Vue.js在前端的应用
    本文介绍了如何使用Net Core 3.0和Web API进行前后端分离开发,并重点探讨了Vue.js在前端的应用。后端采用MySQL数据库和EF Core框架进行数据操作,开发环境为Windows 10和Visual Studio 2019,MySQL服务器版本为8.0.16。文章详细描述了API项目的创建过程、启动步骤以及必要的插件安装,为开发者提供了一套完整的开发指南。 ... [详细]
  • Python错误重试让多少开发者头疼?高效解决方案出炉
    ### 优化后的摘要在处理 Python 开发中的错误重试问题时,许多开发者常常感到困扰。为了应对这一挑战,`tenacity` 库提供了一种高效的解决方案。首先,通过 `pip install tenacity` 安装该库。使用时,可以通过简单的规则配置重试策略。例如,可以设置多个重试条件,使用 `|`(或)和 `&`(与)操作符组合不同的参数,从而实现灵活的错误重试机制。此外,`tenacity` 还支持自定义等待时间、重试次数和异常处理,为开发者提供了强大的工具来提高代码的健壮性和可靠性。 ... [详细]
  • 在对WordPress Duplicator插件0.4.4版本的安全评估中,发现其存在跨站脚本(XSS)攻击漏洞。此漏洞可能被利用进行恶意操作,建议用户及时更新至最新版本以确保系统安全。测试方法仅限于安全研究和教学目的,使用时需自行承担风险。漏洞编号:HTB23162。 ... [详细]
  • 本文以 www.域名.com 为例,详细介绍如何为每个注册用户提供独立的二级域名,如 abc.域名.com。实现这一功能的核心步骤包括:首先,确保域名支持泛解析,即将 A 记录设置为 *.域名.com,以便将所有二级域名请求指向同一服务器。接着,在服务器端使用 ASP.NET 2.0 进行配置,通过解析 HTTP 请求中的主机头信息,动态识别并处理不同的二级域名,从而实现个性化内容展示。此外,还需在数据库中维护用户与二级域名的对应关系,确保每个用户的二级域名都能正确映射到其专属内容。 ... [详细]
  • 本文探讨了在使用 Outlook 时遇到的一个常见问题:无法加载 SAVCORP90 插件,导致软件功能受限。该问题通常表现为在启动 Outlook 时会收到错误提示,影响用户的正常使用体验。文章详细分析了可能的原因,并提供了多种解决方法,包括检查插件兼容性、重新安装插件以及更新 Outlook 版本等。通过这些步骤,用户可以有效解决这一问题,恢复 Outlook 的正常运行。 ... [详细]
  • 在Django中提交表单时遇到值错误问题如何解决?
    在Django项目中,当用户提交包含多个选择目标的表单时,可能会遇到值错误问题。本文将探讨如何通过优化表单处理逻辑和验证机制来有效解决这一问题,确保表单数据的准确性和完整性。 ... [详细]
  • Java能否直接通过HTTP将字节流绕过HEAP写入SD卡? ... [详细]
author-avatar
林禎峰佩蓉
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有