目录:
看起来接口自动化测试什么都比 Web/App 自动化测试要好,为什么还要做 Web/App 自动化测试?
测试类型 | 工具 | 价值 |
---|---|---|
接口抓包 | Charles、Postman | 接口抓包工具,可以抓取 App 的数据包 |
接口测试 | Postman | 接口调试工具,接口手工测试工具,学习成本低,直接安装即可使用 |
接口自动化测试 | Requests、RestAssured | 用于接口自动化测试的 Java、Python 第三方库,需要与对应编程语言结合使用 |
性能测试 | JMeter | 性能测试工具 |
pip install requests
方法 | 说明 |
---|---|
requests.request() | 构造一个请求,支撑以下各方法的基础方法。 |
requests.get() | 构造 HTTP 协议中的 GET 请求。 |
requests.post() | 构造 HTTP 协议中的 POST 请求。 |
requests.put() | 构造 HTTP 协议中的 PUT 请求。 |
requests.delete() | 构造 HTTP 协议中的 DELETE 请求。 |
requests.get(url, params=None, **kwargs)
import requests
def test_get():
# 定义接口的 url 和拼接在 url 中的请求参数
url = "https://httpbin.ceshiren.com/get"
# 发出 GET 请求,r 接收接口响应
r = requests.get(url, verify=False)
# 打印接口响应
print(r.json())
requests.post(url, data=None, json=None, **kwargs)
def test_post():
# 定义接口的 url
url = "https://httpbin.ceshiren.com/post"
# 发出 POST 请求,r 接收接口响应
response = requests.post(url, verify=False)
# 打印接口响应
print(response.text)
requests.put(url, data=None, **kwargs)
def test_put():
# 定义接口的 url
url = "https://httpbin.ceshiren.com/put"
# 发出 POST 请求,r 接收接口响应
r = requests.put(url, verify=False)
# 打印接口响应
print(r)
requests.delete(url, **kwargs)
def test_delete():
# 定义接口的 url 和表单格式请求体
url = "https://httpbin.ceshiren.com/delete"
# 发出 POST 请求,r 接收接口响应
r = requests.delete(url, verify=False)
# 打印接口响应
print(r)
requests.request(method, url, **kwargs)
GET
,OPTIONS
,HEAD
,POST
,PUT
,PATCH
,DELETE
。def request(method, url, **kwargs):
"""Constructs and sends a :class:`Request `.
:param method: method for the new :class:`Request` object: ``GET``, ``OPTIONS``, ``HEAD``, ``POST``, ``PUT``, ``PATCH``, or ``DELETE``.
:param url: URL for the new :class:`Request` object.
:param params: (optional) Dictionary, list of tuples or bytes to send
in the query string for the :class:`Request`.
: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 headers: (optional) Dictionary of HTTP Headers to send with the :class:`Request`.
:param cookies: (optional) Dict or CookieJar object to send with the :class:`Request`.
:param files: (optional) Dictionary of ``'name': file-like-objects`` (or ``{'name': file-tuple}``) for multipart encoding upload.
``file-tuple`` can be a 2-tuple ``('filename', fileobj)``, 3-tuple ``('filename', fileobj, 'content_type')``
or a 4-tuple ``('filename', fileobj, 'content_type', custom_headers)``, where ``'content-type'`` is a string
defining the content type of the given file and ``custom_headers`` a dict-like object containing additional headers
to add for the file.
:param auth: (optional) Auth tuple to enable Basic/Digest/Custom HTTP Auth.
:param timeout: (optional) How many seconds to wait for the server to send data
before giving up, as a float, or a :ref:`(connect timeout, read
timeout) ` tuple.
:type timeout: float or tuple
:param allow_redirects: (optional) Boolean. Enable/disable GET/OPTIONS/POST/PUT/PATCH/DELETE/HEAD redirection. Defaults to ``True``.
:type allow_redirects: bool
:param proxies: (optional) Dictionary mapping protocol to the URL of the proxy.
:param verify: (optional) Either a boolean, in which case it controls whether we verify
the server's TLS certificate, or a string, in which case it must be a path
to a CA bundle to use. Defaults to ``True``.
:param stream: (optional) if ``False``, the response content will be immediately downloaded.
:param cert: (optional) if String, path to ssl client cert file (.pem). If Tuple, ('cert', 'key') pair.
:return: :class:`Response ` object
:rtype: requests.Response
参数 | 应用场景 |
---|---|
method | 请求方法 |
url | 请求 URL |
params | 请求中携带 URL 参数 |
data | 请求中携带请求体(默认为表单请求) |
json | 请求中携带 json 格式的请求体 |
headers | 请求中携带头信息 |
cookies | 请求中携带 cookies |
files | 请求中携带文件格式的请求体 |
auth | 请求中携带认证信息 |
timeout | 设置请求超时时间 |
allow_redirects | 请求是否允许重定向 |
proxies | 设置请求代理 |
verify | 请求是否要认证 |
cert | 请求中携带 ssl 证书 |
?
代表客户端向服务端传递的参数。key=value
形式拼接在 URL 中。&
分隔?username=LiMing&id=666
。requests.get(url, params)
def test_get_by_params():
url = "https://httpbin.ceshiren.com/get"
params = {"get_key": "get_value"}
r = requests.get(url, params=params, verify=False)
print(r.json())
def test_get_by_url():
url = "https://httpbin.ceshiren.com/get?get_key=get_value"
r = requests.get(url, verify=False)
print(r.json())
def test_post_by_params():
url = "https://httpbin.ceshiren.com/post"
params = {"post_key": "post_value"}
r = requests.post(url, params=params,verify=False)
print(r.json())
内容 | 含义 |
---|---|
Authorization | 表示客户端请求的身份验证信息 |
Cookie | 表示客户端的状态信息,通常用于身份验证和会话管理 |
Content-Type | 表示请求消息体的 MIME 类型 |
User-Agent | 发送请求的客户端软件信息 |
headers = {'user-agent': 'my-app/0.0.1'}
r = requests.get(url, headers=headers)
代码示例:
import requests
def test_header():
url = "https://httpbin.ceshiren.com/get"
headers = {"name": "LiMing", "User-Agent": "apple", "Content-type": "application/json"}
r = requests.get(url, verify=False, headers=headers)
print(r.json())
类型 | 介绍 | Content-type |
---|---|---|
JSON(JavaScript Object Notation) | 轻量级的数据交换格式,最常见的一种类型。 | application/json |
表单数据(Form Data) | 以键值对的形式提交数据,例如通过 HTML 表单提交数据。 | application/x-www-form-urlencoded |
XML(eXtensible Markup Language) | 常用的标记语言,通常用于传递配置文件等数据。 | application/xml text/xml |
文件(File) | 可以通过请求体上传文件数据,例如上传图片、视频等文件。 | 上传文件的 MIME 类型,例如 image/jpeg multipart/form-data |
纯文本(Text) | 纯文本数据,例如发送邮件、发送短信等场景 | text/plain |
其他格式 | 二进制数据、protobuf 等格式 |
import requests
def test_request_body():
url = "https://httpbin.ceshiren.com/post"
request_body = {"name": "LiMing", "teacher": 'Jenny'}
r = requests.post(url, verify=False, json=request_body)
print(r.json())
import requests
def test_assert():
url = "https://httpbin.ceshiren.com/get"
r = requests.get(url, verify=False)
print(r)
属性 | 含义 |
---|---|
r |
响应 Response 对象(可以使用任意的变量名) |
r.status_code |
HTTP 响应状态码 |
r.headers |
返回一个字典,包含响应头的所有信息。 |
r.text |
返回响应的内容,是一个字符串。 |
r.url |
编码之后的请求的 url |
r.content |
返回响应的内容,是一个字节流。 |
r.raw |
响应的原始内容 |
r.json() |
如果响应的内容是 JSON 格式,可以使用该方法将其解析成 Python 对象。 |
代码示例:
import requests
def test_assert():
url = "https://httpbin.ceshiren.com/get"
r = requests.get(url, verify=False)
print("------------------------------------------")
print(r.status_code)
print("------------------------------------------")
print(r.headers)
print("------------------------------------------")
print(r.text)
print("------------------------------------------")
print(r.url)
print("------------------------------------------")
print(r.content)
print("------------------------------------------")
print(r.raw)
print("------------------------------------------")
print(r.json())
r.status_code
代码示例:
def test_assert_base():
url = "https://httpbin.ceshiren.com/get"
r = requests.get(url, verify=False)
assert r.status_code == 200
{
"name": "John",
"age": 30,
"city": "New York"
}
r.json()
:返回 python 字典。import requests
def test_res_json():
r = requests.get("https://httpbin.ceshiren.com/get")
assert r.status_code == 200
assert r.json()["url"] == "https://httpbin.ceshiren.com/get"
代码示例:
import requests
def test_resonse_body_json():
url = "https://httpbin.ceshiren.com/get"
r = requests.get(url, verify=False)
print(r.json())
print(r.json()["headers"])
print(r.json()["headers"]["Host"])
assert r.status_code == 200
assert r.json()["url"] == "https://httpbin.ceshiren.com/get"
def test_resonse_body_json_fail():
url = "https://www.baidu.com"
r = requests.get(url)
# 如果响应体是非 json 的场景,那么就不要使用 r.json() 的方式,否则则会# raise RequestsJSONDecodeError
# print(r.json())
# 可以使用r.text
print(r.text)
assert r.status_code == 200
形式 | 章节 | 描述 |
---|---|---|
知识点 | 接口请求方法 | http 接口请求方法构造 |
知识点 | 接口请求参数 | http 接口请求参数构造 |
知识点 | 接口请求体-json | http 接口请求体为 json 格式 |
知识点 | 接口响应断言 | http 接口响应状态码断言 |
test_petstore.py
import requests
from interface_automation_testing.接口自动化测试_L1.宠物商店接口自动化测试实战.utils.log_util import logger
class TestPetstorePetsearch:
def setup_class(self):
# 定义接口请求 URL
self.base_url = "https://petstore.swagger.io/v2/pet"
self.search_url = self.base_url + "/findByStatus"
# "【冒烟】传入available状态可以正确获取该状态下的宠物信息"
def test_search_pet1(self):
# 查询接口请求参数
params = {
"status": "available"
}
# 发出查询请求
r = requests.get(self.search_url, params=params, verify=False)
logger.info('【冒烟】传入available状态可以正确获取该状态下的宠物信息')
# 状态断言
assert r.status_code == 200
# 业务断言
assert r.json() != []
assert "id" in r.json()[0]
项目结构:
import logging
import os
from logging.handlers import RotatingFileHandler
# 绑定绑定句柄到logger对象
logger = logging.getLogger(__name__)
# 获取当前工具文件所在的路径
root_path = os.path.dirname(os.path.abspath(__file__))
# 拼接当前要输出日志的路径
root_len = len(root_path)
strs = root_path[0:root_len-6]
log_dir_path = os.sep.join([strs,f'datas\logs'])
if not os.path.isdir(log_dir_path):
os.mkdir(log_dir_path)
# 创建日志记录器,指明日志保存路径,每个日志的大小,保存日志的上限
file_log_handler = RotatingFileHandler(os.sep.join([log_dir_path, 'log.log']),
maxBytes=1024 * 1024, backupCount=10,
encoding="utf-8")
# 设置日志的格式
date_string = '%Y-%m-%d %H:%M:%S'
formatter = logging.Formatter(
'[%(asctime)s] [%(levelname)s] [%(filename)s]/[line: %(lineno)d]/[%(funcName)s] %(message)s ',
date_string)
# 日志输出到控制台的句柄
stream_handler = logging.StreamHandler()
# 将日志记录器指定日志的格式
file_log_handler.setFormatter(formatter)
stream_handler.setFormatter(formatter)
# 为全局的日志工具对象添加日志记录器
# 绑定绑定句柄到logger对象
logger.addHandler(stream_handler)
logger.addHandler(file_log_handler)
# 设置日志输出级别
logger.setLevel(level=logging.INFO)
def prit_path():
print(root_path)
print(log_dir_path)
prit_path()
test_petstore_parameterization.py
import pytest
import requests
from interface_automation_testing.接口自动化测试_L1.宠物商店接口自动化测试实战.utils.log_util import logger
class TestPetstorePetsearch:
def setup_class(self):
# 定义接口请求 URL
self.base_url = "https://petstore.swagger.io/v2/pet"
self.search_url = self.base_url + "/findByStatus"
# "【冒烟】传入available状态可以正确获取该状态下的宠物信息"
# "【冒烟】传入pending状态可以正确获取该状态下的宠物信息"
# "【冒烟】传入sold状态可以正确获取该状态下的宠物信息"
@pytest.mark.parametrize(
"status",
['available', 'pending', 'sold'],
ids=["available_pets", "pending_pets", "sold_pets"]
)
def test_search_pet1(self, status):
# 查询接口请求参数
params = {
"status": status
}
# 发出查询请求
r = requests.get(self.search_url, params=params, verify=False)
logger.info(f'【冒烟】传入{status}状态可以正确获取该状态下的宠物信息')
# 状态断言
assert r.status_code == 200
# 业务断言
assert r.json() != []
assert "id" in r.json()[0]
# "【异常】传入非指定状态字符串﹐响应为空"
# "【异常】传入非指定状态数字,响应为空"
# "【异常】传入空字符串,响应为空""
@pytest.mark.parametrize(
"status",
['petstatus', '123456', ''],
ids=["petstatus_pets", "123456_pets", "null_pets"]
)
def test_search_pet2(self, status):
# 查询接口请求参数
params = {
"status": status
}
# 发出查询请求
r = requests.get(self.search_url, params=params, verify=False)
logger.info(f'【异常】传入{status}字符串﹐响应为空')
# 状态断言
assert r.status_code == 200
# 业务断言
assert r.json() == []
# 【异常】不传入status,响应为空"
def test_search_pet3(self):
# 查询接口请求参数
# 发出查询请求
r = requests.get(self.search_url, verify=False)
logger.info(f'【异常】不传入status,响应为空')
# 状态断言
assert r.status_code == 200
# 业务断言
assert r.json() == []
# "【异常】传入非status 的参数,响应为空"
def test_search_pet4(self):
# 查询接口请求参数
params = {
"key": "available"
}
# 发出查询请求
r = requests.get(self.search_url, params=params, verify=False)
logger.info(f'【异常】传入非status 的参数,响应为空')
# 状态断言
assert r.status_code == 200
# 业务断言
assert r.json() == []
# 生成报告信息
pytest --alluredir=./report
# 生成报告在线服务,查看报告
allure serve ./report/
pytest -vs .\test_petstore_parameterization.py --alluredir=./datas/report --clean-alluredir
运行结果:
allure serve .\report\
运行结果:
项目地址: template: 用来存放开发模版 - Gitee.com