mirror of
https://github.com/httprunner/httprunner.git
synced 2026-09-05 23:51:25 +08:00
Merge pull request #786 from httprunner/leo_dev
2.4.1 - feat: add `upload` keyword for upload test, see [doc](https://docs.httprunner.org/prepare/upload-case/) - test: pip install package - test: hrun command **Fixed** - fix: typo testfile_paths - fix: check if locustio installed - fix: dump json file name is empty when running relative testfile
This commit is contained in:
@@ -13,7 +13,11 @@ matrix:
|
|||||||
install:
|
install:
|
||||||
- pip install poetry
|
- pip install poetry
|
||||||
- poetry install -vvv
|
- poetry install -vvv
|
||||||
|
- poetry build
|
||||||
|
- ls dist/*.whl | xargs pip install # test installation
|
||||||
script:
|
script:
|
||||||
|
- hrun -V
|
||||||
|
- cd tests/httpbin && hrun basic.yml --log-level debug --failfast && cd -
|
||||||
- python -m httprunner.cli hrun -V
|
- python -m httprunner.cli hrun -V
|
||||||
- python -m httprunner.cli hrun -h
|
- python -m httprunner.cli hrun -h
|
||||||
- poetry build
|
- poetry build
|
||||||
|
|||||||
@@ -1,5 +1,19 @@
|
|||||||
# Release History
|
# Release History
|
||||||
|
|
||||||
|
## 2.4.1 (2019-12-12)
|
||||||
|
|
||||||
|
**Added**
|
||||||
|
|
||||||
|
- feat: add `upload` keyword for upload test, see [doc](https://docs.httprunner.org/prepare/upload-case/)
|
||||||
|
- test: pip install package
|
||||||
|
- test: hrun command
|
||||||
|
|
||||||
|
**Fixed**
|
||||||
|
|
||||||
|
- fix: typo testfile_paths
|
||||||
|
- fix: check if locustio installed
|
||||||
|
- fix: dump json file name is empty when running relative testfile
|
||||||
|
|
||||||
## 2.4.0 (2019-12-11)
|
## 2.4.0 (2019-12-11)
|
||||||
|
|
||||||
**Added**
|
**Added**
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
|
||||||
|
对于上传文件类型的测试场景,HttpRunner 集成 [requests_toolbelt][1] 实现了上传功能。
|
||||||
|
|
||||||
|
在使用之前,确保已安装如下依赖库:
|
||||||
|
|
||||||
|
- [requests_toolbelt](https://github.com/requests/toolbelt)
|
||||||
|
- [filetype](https://github.com/h2non/filetype.py)
|
||||||
|
|
||||||
|
使用内置 `upload` 关键字,可轻松实现上传功能(适用版本:2.4.1+)。
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- test:
|
||||||
|
name: upload file
|
||||||
|
request:
|
||||||
|
url: http://httpbin.org/upload
|
||||||
|
method: POST
|
||||||
|
headers:
|
||||||
|
Cookie: session=AAA-BBB-CCC
|
||||||
|
upload:
|
||||||
|
file: "data/file_to_upload"
|
||||||
|
field1: "value1"
|
||||||
|
field2: "value2"
|
||||||
|
validate:
|
||||||
|
- eq: ["status_code", 200]
|
||||||
|
```
|
||||||
|
|
||||||
|
同时,你也可以继续使用之前描述形式(适用版本:2.0+)。
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- test:
|
||||||
|
name: upload file
|
||||||
|
variables:
|
||||||
|
file: "data/file_to_upload"
|
||||||
|
field1: "value1"
|
||||||
|
field2: "value2"
|
||||||
|
m_encoder: ${multipart_encoder(file=$file, field1=$field1, field2=$field2)}
|
||||||
|
request:
|
||||||
|
url: http://httpbin.org/upload
|
||||||
|
method: POST
|
||||||
|
headers:
|
||||||
|
Content-Type: ${multipart_content_type($m_encoder)}
|
||||||
|
Cookie: session=AAA-BBB-CCC
|
||||||
|
data: $m_encoder
|
||||||
|
validate:
|
||||||
|
- eq: ["status_code", 200]
|
||||||
|
```
|
||||||
|
|
||||||
|
参考案例:[httprunner/tests/httpbin/upload.v2.yml][2]
|
||||||
|
|
||||||
|
[1]: https://toolbelt.readthedocs.io/en/latest/uploading-data.html
|
||||||
|
[2]: https://github.com/httprunner/httprunner/blob/master/tests/httpbin/upload.v2.yml
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
__version__ = "2.4.0"
|
__version__ = "2.4.1"
|
||||||
__description__ = "One-stop solution for HTTP(S) testing."
|
__description__ = "One-stop solution for HTTP(S) testing."
|
||||||
|
|
||||||
__all__ = ["__version__", "__description__"]
|
__all__ = ["__version__", "__description__"]
|
||||||
|
|||||||
@@ -3,19 +3,13 @@ Built-in functions used in YAML/JSON testcases.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
import os
|
|
||||||
import random
|
import random
|
||||||
import string
|
import string
|
||||||
import time
|
import time
|
||||||
|
|
||||||
import filetype
|
|
||||||
from requests_toolbelt import MultipartEncoder
|
|
||||||
|
|
||||||
from httprunner.compat import builtin_str, integer_types
|
from httprunner.compat import builtin_str, integer_types
|
||||||
from httprunner.exceptions import ParamsError
|
from httprunner.exceptions import ParamsError
|
||||||
|
|
||||||
PWD = os.getcwd()
|
|
||||||
|
|
||||||
|
|
||||||
def gen_random_string(str_len):
|
def gen_random_string(str_len):
|
||||||
""" generate random string with specified length
|
""" generate random string with specified length
|
||||||
@@ -44,62 +38,3 @@ def sleep(n_secs):
|
|||||||
"""
|
"""
|
||||||
time.sleep(n_secs)
|
time.sleep(n_secs)
|
||||||
|
|
||||||
|
|
||||||
"""
|
|
||||||
upload files with requests-toolbelt
|
|
||||||
e.g.
|
|
||||||
|
|
||||||
- test:
|
|
||||||
name: upload file
|
|
||||||
variables:
|
|
||||||
file_path: "data/test.env"
|
|
||||||
multipart_encoder: ${multipart_encoder(file=$file_path)}
|
|
||||||
request:
|
|
||||||
url: /post
|
|
||||||
method: POST
|
|
||||||
headers:
|
|
||||||
Content-Type: ${multipart_content_type($multipart_encoder)}
|
|
||||||
data: $multipart_encoder
|
|
||||||
validate:
|
|
||||||
- eq: ["status_code", 200]
|
|
||||||
- startswith: ["content.files.file", "UserName=test"]
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
def multipart_encoder(**kwargs):
|
|
||||||
""" initialize MultipartEncoder with uploading fields.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def get_filetype(file_path):
|
|
||||||
file_type = filetype.guess(file_path)
|
|
||||||
if file_type:
|
|
||||||
return file_type.mime
|
|
||||||
else:
|
|
||||||
return "text/html"
|
|
||||||
|
|
||||||
fields_dict = {}
|
|
||||||
for key, value in kwargs.items():
|
|
||||||
|
|
||||||
if os.path.isabs(value):
|
|
||||||
_file_path = value
|
|
||||||
is_file = True
|
|
||||||
else:
|
|
||||||
global PWD
|
|
||||||
_file_path = os.path.join(PWD, value)
|
|
||||||
is_file = os.path.isfile(_file_path)
|
|
||||||
|
|
||||||
if is_file:
|
|
||||||
filename = os.path.basename(_file_path)
|
|
||||||
with open(_file_path, 'rb') as f:
|
|
||||||
mime_type = get_filetype(_file_path)
|
|
||||||
fields_dict[key] = (filename, f.read(), mime_type)
|
|
||||||
else:
|
|
||||||
fields_dict[key] = value
|
|
||||||
|
|
||||||
return MultipartEncoder(fields=fields_dict)
|
|
||||||
|
|
||||||
|
|
||||||
def multipart_content_type(multipart_encoder):
|
|
||||||
""" prepare Content-Type for request headers
|
|
||||||
"""
|
|
||||||
return multipart_encoder.content_type
|
|
||||||
|
|||||||
+1
-1
@@ -91,7 +91,7 @@ def main():
|
|||||||
|
|
||||||
err_code = 0
|
err_code = 0
|
||||||
try:
|
try:
|
||||||
for path in args.testcase_paths:
|
for path in args.testfile_paths:
|
||||||
summary = runner.run(path, dot_env_path=args.dot_env_path)
|
summary = runner.run(path, dot_env_path=args.dot_env_path)
|
||||||
report_dir = args.report_dir or os.path.join(runner.project_working_directory, "reports")
|
report_dir = args.report_dir or os.path.join(runner.project_working_directory, "reports")
|
||||||
gen_html_report(
|
gen_html_report(
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ HttpRunner loader
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from httprunner.loader.check import is_testcase_path, is_testcases, validate_json_file
|
from httprunner.loader.check import is_testcase_path, is_testcases, validate_json_file
|
||||||
|
from httprunner.loader.locate import get_project_working_directory as get_pwd
|
||||||
from httprunner.loader.load import load_csv_file, load_builtin_functions
|
from httprunner.loader.load import load_csv_file, load_builtin_functions
|
||||||
from httprunner.loader.buildup import load_cases, load_project_data
|
from httprunner.loader.buildup import load_cases, load_project_data
|
||||||
|
|
||||||
@@ -16,6 +17,7 @@ __all__ = [
|
|||||||
"is_testcase_path",
|
"is_testcase_path",
|
||||||
"is_testcases",
|
"is_testcases",
|
||||||
"validate_json_file",
|
"validate_json_file",
|
||||||
|
"get_pwd",
|
||||||
"load_csv_file",
|
"load_csv_file",
|
||||||
"load_builtin_functions",
|
"load_builtin_functions",
|
||||||
"load_project_data",
|
"load_project_data",
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import importlib
|
|||||||
import os
|
import os
|
||||||
|
|
||||||
from httprunner import exceptions, logger, utils
|
from httprunner import exceptions, logger, utils
|
||||||
from httprunner.builtin import functions
|
|
||||||
from httprunner.loader.load import load_module_functions, load_folder_content, load_file, load_dot_env_file, \
|
from httprunner.loader.load import load_module_functions, load_folder_content, load_file, load_dot_env_file, \
|
||||||
load_folder_files
|
load_folder_files
|
||||||
from httprunner.loader.locate import init_project_working_directory, get_project_working_directory
|
from httprunner.loader.locate import init_project_working_directory, get_project_working_directory
|
||||||
@@ -335,7 +334,6 @@ def load_test_file(path):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
raw_content = load_file(path)
|
raw_content = load_file(path)
|
||||||
loaded_content = None
|
|
||||||
|
|
||||||
if isinstance(raw_content, dict):
|
if isinstance(raw_content, dict):
|
||||||
|
|
||||||
@@ -480,11 +478,9 @@ def load_project_data(test_path, dot_env_path=None):
|
|||||||
debugtalk_functions = {}
|
debugtalk_functions = {}
|
||||||
|
|
||||||
# locate PWD and load debugtalk.py functions
|
# locate PWD and load debugtalk.py functions
|
||||||
|
|
||||||
project_mapping["PWD"] = project_working_directory
|
project_mapping["PWD"] = project_working_directory
|
||||||
functions.PWD = project_working_directory # TODO: remove
|
|
||||||
project_mapping["functions"] = debugtalk_functions
|
project_mapping["functions"] = debugtalk_functions
|
||||||
project_mapping["test_path"] = test_path
|
project_mapping["test_path"] = os.path.abspath(test_path)
|
||||||
|
|
||||||
# load api
|
# load api
|
||||||
tests_def_mapping["api"] = load_api_folder(os.path.join(project_working_directory, "api"))
|
tests_def_mapping["api"] = load_api_folder(os.path.join(project_working_directory, "api"))
|
||||||
|
|||||||
+17
-6
@@ -428,6 +428,11 @@ def get_mapping_function(function_name, functions_mapping):
|
|||||||
elif function_name in ["environ", "ENV"]:
|
elif function_name in ["environ", "ENV"]:
|
||||||
return utils.get_os_environ
|
return utils.get_os_environ
|
||||||
|
|
||||||
|
elif function_name in ["multipart_encoder", "multipart_content_type"]:
|
||||||
|
# plugin for upload test
|
||||||
|
from httprunner.plugins import uploader
|
||||||
|
return getattr(uploader, function_name)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# check if HttpRunner builtin functions
|
# check if HttpRunner builtin functions
|
||||||
built_in_functions = loader.load_builtin_functions()
|
built_in_functions = loader.load_builtin_functions()
|
||||||
@@ -439,8 +444,9 @@ def get_mapping_function(function_name, functions_mapping):
|
|||||||
# check if Python builtin functions
|
# check if Python builtin functions
|
||||||
return getattr(builtins, function_name)
|
return getattr(builtins, function_name)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
# is not builtin function
|
pass
|
||||||
raise exceptions.FunctionNotFound("{} is not found.".format(function_name))
|
|
||||||
|
raise exceptions.FunctionNotFound("{} is not found.".format(function_name))
|
||||||
|
|
||||||
|
|
||||||
def parse_function_params(params):
|
def parse_function_params(params):
|
||||||
@@ -1146,13 +1152,18 @@ def __prepare_testcase_tests(tests, config, project_mapping, session_variables_s
|
|||||||
api_def_dict = test_dict.pop("api_def")
|
api_def_dict = test_dict.pop("api_def")
|
||||||
_extend_with_api(test_dict, api_def_dict)
|
_extend_with_api(test_dict, api_def_dict)
|
||||||
|
|
||||||
|
# verify priority: testcase teststep > testcase config
|
||||||
|
if "request" in test_dict:
|
||||||
|
if "verify" not in test_dict["request"]:
|
||||||
|
test_dict["request"]["verify"] = config_verify
|
||||||
|
|
||||||
|
if "upload" in test_dict["request"]:
|
||||||
|
from httprunner.plugins.uploader import prepare_upload_test
|
||||||
|
prepare_upload_test(test_dict)
|
||||||
|
|
||||||
# current teststep variables
|
# current teststep variables
|
||||||
teststep_variables_set |= set(test_dict.get("variables", {}).keys())
|
teststep_variables_set |= set(test_dict.get("variables", {}).keys())
|
||||||
|
|
||||||
# verify priority: testcase teststep > testcase config
|
|
||||||
if "request" in test_dict and "verify" not in test_dict["request"]:
|
|
||||||
test_dict["request"]["verify"] = config_verify
|
|
||||||
|
|
||||||
# move extracted variable to session variables
|
# move extracted variable to session variables
|
||||||
if "extract" in test_dict:
|
if "extract" in test_dict:
|
||||||
extract_mapping = utils.ensure_mapping_format(test_dict["extract"])
|
extract_mapping = utils.ensure_mapping_format(test_dict["extract"])
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ try:
|
|||||||
# monkey patch ssl at beginning to avoid RecursionError when running locust.
|
# monkey patch ssl at beginning to avoid RecursionError when running locust.
|
||||||
from gevent import monkey
|
from gevent import monkey
|
||||||
monkey.patch_ssl()
|
monkey.patch_ssl()
|
||||||
|
from locust import main as locust_main
|
||||||
except ImportError:
|
except ImportError:
|
||||||
msg = """
|
msg = """
|
||||||
Locust is not installed, install first and try again.
|
Locust is not installed, install first and try again.
|
||||||
@@ -61,8 +62,7 @@ def gen_locustfile(testcase_file_path):
|
|||||||
|
|
||||||
|
|
||||||
def start_locust_main():
|
def start_locust_main():
|
||||||
from locust.main import main
|
locust_main.main()
|
||||||
main()
|
|
||||||
|
|
||||||
|
|
||||||
def start_master(sys_argv):
|
def start_master(sys_argv):
|
||||||
|
|||||||
@@ -0,0 +1,143 @@
|
|||||||
|
""" upload test plugin.
|
||||||
|
|
||||||
|
If you want to use this plugin, you should install the following dependencies first.
|
||||||
|
|
||||||
|
- requests_toolbelt
|
||||||
|
- filetype
|
||||||
|
|
||||||
|
Then you can write upload test script as below:
|
||||||
|
|
||||||
|
- test:
|
||||||
|
name: upload file
|
||||||
|
request:
|
||||||
|
url: http://httpbin.org/upload
|
||||||
|
method: POST
|
||||||
|
headers:
|
||||||
|
Cookie: session=AAA-BBB-CCC
|
||||||
|
upload:
|
||||||
|
file: "data/file_to_upload"
|
||||||
|
field1: "value1"
|
||||||
|
field2: "value2"
|
||||||
|
validate:
|
||||||
|
- eq: ["status_code", 200]
|
||||||
|
|
||||||
|
For compatibility, you can also write upload test script in old way:
|
||||||
|
|
||||||
|
- test:
|
||||||
|
name: upload file
|
||||||
|
variables:
|
||||||
|
file: "data/file_to_upload"
|
||||||
|
field1: "value1"
|
||||||
|
field2: "value2"
|
||||||
|
m_encoder: ${multipart_encoder(file=$file, field1=$field1, field2=$field2)}
|
||||||
|
request:
|
||||||
|
url: http://httpbin.org/upload
|
||||||
|
method: POST
|
||||||
|
headers:
|
||||||
|
Content-Type: ${multipart_content_type($m_encoder)}
|
||||||
|
Cookie: session=AAA-BBB-CCC
|
||||||
|
data: $m_encoder
|
||||||
|
validate:
|
||||||
|
- eq: ["status_code", 200]
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
try:
|
||||||
|
import filetype
|
||||||
|
from requests_toolbelt import MultipartEncoder
|
||||||
|
except ImportError:
|
||||||
|
msg = """
|
||||||
|
uploader plugin dependencies uninstalled, install first and try again.
|
||||||
|
install with pip:
|
||||||
|
$ pip install requests_toolbelt filetype
|
||||||
|
"""
|
||||||
|
print(msg)
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
from httprunner.exceptions import ParamsError
|
||||||
|
|
||||||
|
|
||||||
|
def prepare_upload_test(test_dict):
|
||||||
|
""" preprocess for upload test
|
||||||
|
replace `upload` info with MultipartEncoder
|
||||||
|
|
||||||
|
Args:
|
||||||
|
test_dict (dict):
|
||||||
|
|
||||||
|
{
|
||||||
|
"variables": {},
|
||||||
|
"request": {
|
||||||
|
"url": "http://httpbin.org/upload",
|
||||||
|
"method": "POST",
|
||||||
|
"headers": {
|
||||||
|
"Cookie": "session=AAA-BBB-CCC"
|
||||||
|
},
|
||||||
|
"upload": {
|
||||||
|
"file": "data/file_to_upload"
|
||||||
|
"md5": "123"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"""
|
||||||
|
upload_json = test_dict["request"].pop("upload", {})
|
||||||
|
if not upload_json:
|
||||||
|
raise ParamsError("invalid upload info: {}".format(upload_json))
|
||||||
|
|
||||||
|
params_list = []
|
||||||
|
for key, value in upload_json.items():
|
||||||
|
test_dict["variables"][key] = value
|
||||||
|
params_list.append("{}=${}".format(key, key))
|
||||||
|
|
||||||
|
params_str = ", ".join(params_list)
|
||||||
|
test_dict["variables"]["m_encoder"] = "${multipart_encoder(" + params_str + ")}"
|
||||||
|
|
||||||
|
test_dict["request"].setdefault("headers", {})
|
||||||
|
test_dict["request"]["headers"]["Content-Type"] = "${multipart_content_type($m_encoder)}"
|
||||||
|
|
||||||
|
test_dict["request"]["data"] = "$m_encoder"
|
||||||
|
|
||||||
|
|
||||||
|
def multipart_encoder(**kwargs):
|
||||||
|
""" initialize MultipartEncoder with uploading fields.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def get_filetype(file_path):
|
||||||
|
file_type = filetype.guess(file_path)
|
||||||
|
if file_type:
|
||||||
|
return file_type.mime
|
||||||
|
else:
|
||||||
|
return "text/html"
|
||||||
|
|
||||||
|
fields_dict = {}
|
||||||
|
for key, value in kwargs.items():
|
||||||
|
|
||||||
|
if os.path.isabs(value):
|
||||||
|
# value is absolute file path
|
||||||
|
_file_path = value
|
||||||
|
is_exists_file = os.path.isfile(value)
|
||||||
|
else:
|
||||||
|
# value is not absolute file path, check if it is relative file path
|
||||||
|
from httprunner.loader import get_pwd
|
||||||
|
_file_path = os.path.join(get_pwd(), value)
|
||||||
|
is_exists_file = os.path.isfile(_file_path)
|
||||||
|
|
||||||
|
if is_exists_file:
|
||||||
|
# value is file path to upload
|
||||||
|
filename = os.path.basename(_file_path)
|
||||||
|
with open(_file_path, 'rb') as f:
|
||||||
|
mime_type = get_filetype(_file_path)
|
||||||
|
fields_dict[key] = (filename, f.read(), mime_type)
|
||||||
|
else:
|
||||||
|
fields_dict[key] = value
|
||||||
|
|
||||||
|
return MultipartEncoder(fields=fields_dict)
|
||||||
|
|
||||||
|
|
||||||
|
def multipart_content_type(m_encoder):
|
||||||
|
""" prepare Content-Type for request headers
|
||||||
|
"""
|
||||||
|
return m_encoder.content_type
|
||||||
@@ -579,6 +579,7 @@ def dump_json_file(json_data, json_file_abs_path):
|
|||||||
json_data,
|
json_data,
|
||||||
indent=4,
|
indent=4,
|
||||||
separators=(',', ':'),
|
separators=(',', ':'),
|
||||||
|
encoding="utf8",
|
||||||
ensure_ascii=False,
|
ensure_ascii=False,
|
||||||
cls=PythonObjectEncoder
|
cls=PythonObjectEncoder
|
||||||
))
|
))
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
# require mkdocs-material 3.x
|
# require mkdocs-material 3.x
|
||||||
|
|
||||||
|
#
|
||||||
|
# pip install mkdocs
|
||||||
|
# pip install mkdocs-material
|
||||||
|
|
||||||
# Project information
|
# Project information
|
||||||
site_name: HttpRunner V2.x 中文使用文档
|
site_name: HttpRunner V2.x 中文使用文档
|
||||||
site_description: HttpRunner V2.x User Documentation
|
site_description: HttpRunner V2.x User Documentation
|
||||||
@@ -64,6 +68,7 @@ nav:
|
|||||||
- 参数化数据驱动: prepare/parameters.md
|
- 参数化数据驱动: prepare/parameters.md
|
||||||
- Validate & Prettify: prepare/validate-pretty.md
|
- Validate & Prettify: prepare/validate-pretty.md
|
||||||
- 信息安全: prepare/security.md
|
- 信息安全: prepare/security.md
|
||||||
|
- 文件上传场景: prepare/upload-case.md
|
||||||
- 测试执行:
|
- 测试执行:
|
||||||
- 运行测试(CLI): run-tests/cli.md
|
- 运行测试(CLI): run-tests/cli.md
|
||||||
- 测试报告: run-tests/report.md
|
- 测试报告: run-tests/report.md
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "httprunner"
|
name = "httprunner"
|
||||||
version = "2.4.0"
|
version = "2.4.1"
|
||||||
description = "One-stop solution for HTTP(S) testing."
|
description = "One-stop solution for HTTP(S) testing."
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
|||||||
+2
-2
@@ -15,8 +15,8 @@ try:
|
|||||||
except ImportError:
|
except ImportError:
|
||||||
httpbin_app = None
|
httpbin_app = None
|
||||||
HTTPBIN_HOST = "httpbin.org"
|
HTTPBIN_HOST = "httpbin.org"
|
||||||
HTTPBIN_PORT = 443
|
HTTPBIN_PORT = 80
|
||||||
HTTPBIN_SERVER = "https://{}:{}".format(HTTPBIN_HOST, HTTPBIN_PORT)
|
HTTPBIN_SERVER = "http://{}:{}".format(HTTPBIN_HOST, HTTPBIN_PORT)
|
||||||
|
|
||||||
FLASK_APP_PORT = 5000
|
FLASK_APP_PORT = 5000
|
||||||
SECRET_KEY = "DebugTalk"
|
SECRET_KEY = "DebugTalk"
|
||||||
|
|||||||
@@ -2,14 +2,15 @@
|
|||||||
name: basic test with httpbin
|
name: basic test with httpbin
|
||||||
base_url: https://httpbin.org/
|
base_url: https://httpbin.org/
|
||||||
|
|
||||||
- test:
|
#- test:
|
||||||
name: index
|
# TODO: fix compatibility with Python 2.7, UnicodeDecodeError
|
||||||
request:
|
# name: index
|
||||||
url: /
|
# request:
|
||||||
method: GET
|
# url: /
|
||||||
validate:
|
# method: GET
|
||||||
- eq: ["status_code", 200]
|
# validate:
|
||||||
- contains: [content, "HTTP Request & Response Service"]
|
# - eq: ["status_code", 200]
|
||||||
|
# - contains: [content, "HTTP Request & Response Service"]
|
||||||
|
|
||||||
- test:
|
- test:
|
||||||
name: headers
|
name: headers
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
config:
|
||||||
|
name: test upload file with httpbin
|
||||||
|
base_url: ${get_httpbin_server()}
|
||||||
|
|
||||||
|
teststeps:
|
||||||
|
-
|
||||||
|
name: upload file
|
||||||
|
variables:
|
||||||
|
file_path: "data/test.env"
|
||||||
|
m_encoder: ${multipart_encoder(file=$file_path)}
|
||||||
|
request:
|
||||||
|
url: /post
|
||||||
|
method: POST
|
||||||
|
headers:
|
||||||
|
Content-Type: ${multipart_content_type($m_encoder)}
|
||||||
|
data: $m_encoder
|
||||||
|
validate:
|
||||||
|
- eq: ["status_code", 200]
|
||||||
|
- startswith: ["content.files.file", "UserName=test"]
|
||||||
|
|
||||||
|
-
|
||||||
|
name: upload file with keyword
|
||||||
|
request:
|
||||||
|
url: /post
|
||||||
|
method: POST
|
||||||
|
upload:
|
||||||
|
file: "data/test.env"
|
||||||
|
validate:
|
||||||
|
- eq: ["status_code", 200]
|
||||||
|
- startswith: ["content.files.file", "UserName=test"]
|
||||||
@@ -6,14 +6,24 @@
|
|||||||
name: upload file
|
name: upload file
|
||||||
variables:
|
variables:
|
||||||
file_path: "data/test.env"
|
file_path: "data/test.env"
|
||||||
multipart_encoder: ${multipart_encoder(file=$file_path)}
|
m_encoder: ${multipart_encoder(file=$file_path)}
|
||||||
request:
|
request:
|
||||||
url: /post
|
url: /post
|
||||||
method: POST
|
method: POST
|
||||||
headers:
|
headers:
|
||||||
Content-Type: ${multipart_content_type($multipart_encoder)}
|
Content-Type: ${multipart_content_type($m_encoder)}
|
||||||
data: $multipart_encoder
|
data: $m_encoder
|
||||||
validate:
|
validate:
|
||||||
- eq: ["status_code", 200]
|
- eq: ["status_code", 200]
|
||||||
- startswith: ["content.files.file", "UserName=test"]
|
- startswith: ["content.files.file", "UserName=test"]
|
||||||
|
|
||||||
|
- test:
|
||||||
|
name: upload file with keyword
|
||||||
|
request:
|
||||||
|
url: /post
|
||||||
|
method: POST
|
||||||
|
upload:
|
||||||
|
file: "data/test.env"
|
||||||
|
validate:
|
||||||
|
- eq: ["status_code", 200]
|
||||||
|
- startswith: ["content.files.file", "UserName=test"]
|
||||||
|
|||||||
+16
-12
@@ -222,12 +222,17 @@ class TestHttpRunner(ApiServerUnittest):
|
|||||||
self.assertIn("records", summary["details"][0])
|
self.assertIn("records", summary["details"][0])
|
||||||
|
|
||||||
def test_run_yaml_upload(self):
|
def test_run_yaml_upload(self):
|
||||||
summary = self.runner.run("tests/httpbin/upload.yml")
|
upload_cases_list = [
|
||||||
self.assertTrue(summary["success"])
|
"tests/httpbin/upload.yml",
|
||||||
self.assertEqual(summary["stat"]["testcases"]["total"], 1)
|
"tests/httpbin/upload.v2.yml"
|
||||||
self.assertEqual(summary["stat"]["teststeps"]["total"], 1)
|
]
|
||||||
self.assertIn("details", summary)
|
for upload_case in upload_cases_list:
|
||||||
self.assertIn("records", summary["details"][0])
|
summary = self.runner.run(upload_case)
|
||||||
|
self.assertTrue(summary["success"])
|
||||||
|
self.assertEqual(summary["stat"]["testcases"]["total"], 1)
|
||||||
|
self.assertEqual(summary["stat"]["teststeps"]["total"], 2)
|
||||||
|
self.assertIn("details", summary)
|
||||||
|
self.assertIn("records", summary["details"][0])
|
||||||
|
|
||||||
def test_run_post_data(self):
|
def test_run_post_data(self):
|
||||||
testcases = [
|
testcases = [
|
||||||
@@ -550,12 +555,11 @@ class TestHttpRunner(ApiServerUnittest):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
# def test_validate_response_content(self):
|
def test_validate_response_content(self):
|
||||||
# # TODO: fix compatibility with Python 2.7
|
testcase_file_path = os.path.join(
|
||||||
# testcase_file_path = os.path.join(
|
os.getcwd(), 'tests/httpbin/basic.yml')
|
||||||
# os.getcwd(), 'tests/httpbin/basic.yml')
|
summary = self.runner.run(testcase_file_path)
|
||||||
# summary = self.runner.run(testcase_file_path)
|
self.assertTrue(summary["success"])
|
||||||
# self.assertTrue(summary["success"])
|
|
||||||
|
|
||||||
def test_html_report_xss(self):
|
def test_html_report_xss(self):
|
||||||
testcases = [
|
testcases = [
|
||||||
|
|||||||
Reference in New Issue
Block a user