目录:
看起来接口自动化测试什么都比 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\
运行结果: