feat: implement upload keyword

This commit is contained in:
debugtalk
2020-05-16 16:34:06 +08:00
parent 57e2407461
commit 2647456832
12 changed files with 384 additions and 41 deletions
+89
View File
@@ -0,0 +1,89 @@
config:
name: basic test with httpbin
base_url: https://httpbin.org/
teststeps:
-
name: headers
request:
url: /headers
method: GET
validate:
- eq: ["status_code", 200]
- eq: [body.headers.Host, "httpbin.org"]
-
name: user-agent
request:
url: /user-agent
method: GET
validate:
- eq: ["status_code", 200]
# - startswith: [body.user-agent, "python-requests"]
-
name: get without params
request:
url: /get
method: GET
validate:
- eq: ["status_code", 200]
- eq: [body.args, {}]
-
name: get with params in url
request:
url: /get?a=1&b=2
method: GET
validate:
- eq: ["status_code", 200]
- eq: [body.args, {'a': '1', 'b': '2'}]
-
name: get with params in params field
request:
url: /get
params:
a: 1
b: 2
method: GET
validate:
- eq: ["status_code", 200]
- eq: [body.args, {'a': '1', 'b': '2'}]
-
name: set cookie
request:
url: /cookies/set?name=value
method: GET
validate:
- eq: ["status_code", 200]
# - eq: [cookies.name, "value"]
-
name: extract cookie
request:
url: /cookies
method: GET
validate:
- eq: ["status_code", 200]
# - eq: [cookies.name, "value"]
-
name: post data
request:
url: /post
method: POST
headers:
Content-Type: application/json
data: abc
validate:
- eq: ["status_code", 200]
-
name: validate body length
request:
url: /spec.json
method: GET
validate:
- len_eq: ["body", 9]
+96
View File
@@ -0,0 +1,96 @@
# NOTICE: Generated By HttpRunner. DO'NOT EDIT!
from httprunner import HttpRunner, TConfig, TStep
class TestCaseBasic(HttpRunner):
config = TConfig(
**{
"name": "basic test with httpbin",
"base_url": "https://httpbin.org/",
"path": "examples/httpbin/basic_test.py",
}
)
teststeps = [
TStep(
**{
"name": "headers",
"request": {"url": "/headers", "method": "GET"},
"validate": [
{"eq": ["status_code", 200]},
{"eq": ["body.headers.Host", "httpbin.org"]},
],
}
),
TStep(
**{
"name": "user-agent",
"request": {"url": "/user-agent", "method": "GET"},
"validate": [{"eq": ["status_code", 200]}],
}
),
TStep(
**{
"name": "get without params",
"request": {"url": "/get", "method": "GET"},
"validate": [{"eq": ["status_code", 200]}, {"eq": ["body.args", {}]}],
}
),
TStep(
**{
"name": "get with params in url",
"request": {"url": "/get?a=1&b=2", "method": "GET"},
"validate": [
{"eq": ["status_code", 200]},
{"eq": ["body.args", {"a": "1", "b": "2"}]},
],
}
),
TStep(
**{
"name": "get with params in params field",
"request": {"url": "/get", "params": {"a": 1, "b": 2}, "method": "GET"},
"validate": [
{"eq": ["status_code", 200]},
{"eq": ["body.args", {"a": "1", "b": "2"}]},
],
}
),
TStep(
**{
"name": "set cookie",
"request": {"url": "/cookies/set?name=value", "method": "GET"},
"validate": [{"eq": ["status_code", 200]}],
}
),
TStep(
**{
"name": "extract cookie",
"request": {"url": "/cookies", "method": "GET"},
"validate": [{"eq": ["status_code", 200]}],
}
),
TStep(
**{
"name": "post data",
"request": {
"url": "/post",
"method": "POST",
"headers": {"Content-Type": "application/json"},
"data": "abc",
},
"validate": [{"eq": ["status_code", 200]}],
}
),
TStep(
**{
"name": "validate body length",
"request": {"url": "/spec.json", "method": "GET"},
"validate": [{"len_eq": ["body", 9]}],
}
),
]
if __name__ == "__main__":
TestCaseBasic().test_start()
+30
View File
@@ -0,0 +1,30 @@
config:
name: test upload file with httpbin
base_url: ${get_httpbin_server()}
teststeps:
-
name: upload file
variables:
file_path: "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: ["body.files.file", "UserName=test"]
-
name: upload file with keyword
request:
url: /post
method: POST
upload:
file: "test.env"
validate:
- eq: ["status_code", 200]
- startswith: ["body.files.file", "UserName=test"]
+54
View File
@@ -0,0 +1,54 @@
# NOTICE: Generated By HttpRunner. DO'NOT EDIT!
from httprunner import HttpRunner, TConfig, TStep
class TestCaseUpload(HttpRunner):
config = TConfig(
**{
"name": "test upload file with httpbin",
"base_url": "${get_httpbin_server()}",
"path": "examples/httpbin/upload_test.py",
}
)
teststeps = [
TStep(
**{
"name": "upload file",
"variables": {
"file_path": "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": ["body.files.file", "UserName=test"]},
],
}
),
TStep(
**{
"name": "upload file with keyword",
"request": {
"url": "/post",
"method": "POST",
"upload": {"file": "test.env"},
},
"validate": [
{"eq": ["status_code", 200]},
{"startswith": ["body.files.file", "UserName=test"]},
],
}
),
]
if __name__ == "__main__":
TestCaseUpload().test_start()
+35
View File
@@ -0,0 +1,35 @@
config:
name: basic test with httpbin
base_url: http://httpbin.org/
teststeps:
-
name: validate response with json path
request:
url: /get
params:
a: 1
b: 2
method: GET
validate:
- eq: ["status_code", 200]
- eq: ["body.args.a", 1]
- eq: ["body.args.b", 2]
validate_script:
- "assert status_code == 200"
-
name: validate response with python script
request:
url: /get
params:
a: 1
b: 2
method: GET
validate:
- eq: ["status_code", 200]
validate_script:
- "assert status_code == 201"
- "a = response_json.get('args').get('a')"
- "assert a == '1'"
+43
View File
@@ -0,0 +1,43 @@
# NOTICE: Generated By HttpRunner. DO'NOT EDIT!
from httprunner import HttpRunner, TConfig, TStep
class TestCaseValidate(HttpRunner):
config = TConfig(
**{
"name": "basic test with httpbin",
"base_url": "http://httpbin.org/",
"path": "examples/httpbin/validate_test.py",
}
)
teststeps = [
TStep(
**{
"name": "validate response with json path",
"request": {"url": "/get", "params": {"a": 1, "b": 2}, "method": "GET"},
"validate": [
{"eq": ["status_code", 200]},
{"eq": ["body.args.a", 1]},
{"eq": ["body.args.b", 2]},
],
"validate_script": ["assert status_code == 200"],
}
),
TStep(
**{
"name": "validate response with python script",
"request": {"url": "/get", "params": {"a": 1, "b": 2}, "method": "GET"},
"validate": [{"eq": ["status_code", 200]}],
"validate_script": [
"assert status_code == 201",
"a = response_json.get('args').get('a')",
"assert a == '1'",
],
}
),
]
if __name__ == "__main__":
TestCaseValidate().test_start()
+11 -11
View File
@@ -36,22 +36,22 @@ def get_req_resp_record(resp_obj: Response) -> ReqRespData:
logger.debug(msg) logger.debug(msg)
# record actual request info # record actual request info
request_headers = dict(resp_obj.request.headers)
request_body = resp_obj.request.body
if request_body:
request_content_type = lower_dict_keys(request_headers).get("content-type")
if request_content_type and "multipart/form-data" in request_content_type:
# upload file type
request_body = "upload file stream (OMITTED)"
request_data = RequestData( request_data = RequestData(
method=resp_obj.request.method, method=resp_obj.request.method,
url=resp_obj.request.url, url=resp_obj.request.url,
headers=dict(resp_obj.request.headers), headers=request_headers,
body=resp_obj.request.body, body=request_body,
) )
request_body = resp_obj.request.body
if request_body:
request_content_type = lower_dict_keys(request_data.headers).get("content-type")
if request_content_type and "multipart/form-data" in request_content_type:
# upload file type
request_data.body = "upload file stream (OMITTED)"
else:
request_data.body = request_body
# log request details in debug mode # log request details in debug mode
log_print(request_data, "request") log_print(request_data, "request")
+16 -16
View File
@@ -45,6 +45,9 @@ For compatibility, you can also write upload test script in old way:
import os import os
import sys import sys
from httprunner.parser import parse_variables_mapping
from httprunner.schema import TStep, FunctionsMapping
try: try:
import filetype import filetype
from requests_toolbelt import MultipartEncoder from requests_toolbelt import MultipartEncoder
@@ -57,16 +60,13 @@ $ pip install requests_toolbelt filetype
print(msg) print(msg)
sys.exit(0) sys.exit(0)
from httprunner.exceptions import ParamsError
def prepare_upload_step(step: TStep, functions: FunctionsMapping):
def prepare_upload_test(test_dict):
""" preprocess for upload test """ preprocess for upload test
replace `upload` info with MultipartEncoder replace `upload` info with MultipartEncoder
Args: Args:
test_dict (dict): step: teststep
{ {
"variables": {}, "variables": {},
"request": { "request": {
@@ -81,26 +81,26 @@ def prepare_upload_test(test_dict):
} }
} }
} }
functions: functions mapping
""" """
upload_json = test_dict["request"].pop("upload", {}) if not step.request.upload:
if not upload_json: return
raise ParamsError(f"invalid upload info: {upload_json}")
params_list = [] params_list = []
for key, value in upload_json.items(): for key, value in step.request.upload.items():
test_dict["variables"][key] = value step.variables[key] = value
params_list.append(f"{key}=${key}") params_list.append(f"{key}=${key}")
params_str = ", ".join(params_list) params_str = ", ".join(params_list)
test_dict["variables"]["m_encoder"] = "${multipart_encoder(" + params_str + ")}" step.variables["m_encoder"] = "${multipart_encoder(" + params_str + ")}"
test_dict["request"].setdefault("headers", {}) # parse variables
test_dict["request"]["headers"][ step.variables = parse_variables_mapping(step.variables, functions)
"Content-Type"
] = "${multipart_content_type($m_encoder)}"
test_dict["request"]["data"] = "$m_encoder" step.request.headers["Content-Type"] = "${multipart_content_type($m_encoder)}"
step.request.data = "$m_encoder"
def multipart_encoder(**kwargs): def multipart_encoder(**kwargs):
-1
View File
@@ -125,4 +125,3 @@ class TestLoader(unittest.TestCase):
loader.locate_file("examples/httpbin/", "debugtalk.py"), loader.locate_file("examples/httpbin/", "debugtalk.py"),
os.path.join(os.getcwd(), "examples/httpbin/debugtalk.py"), os.path.join(os.getcwd(), "examples/httpbin/debugtalk.py"),
) )
+3
View File
@@ -8,6 +8,7 @@ from loguru import logger
from httprunner import utils, exceptions from httprunner import utils, exceptions
from httprunner.client import HttpSession from httprunner.client import HttpSession
from httprunner.exceptions import ValidationFailure, ParamsError from httprunner.exceptions import ValidationFailure, ParamsError
from httprunner.ext.uploader import prepare_upload_step
from httprunner.loader import load_project_meta, load_testcase_file from httprunner.loader import load_project_meta, load_testcase_file
from httprunner.parser import build_url, parse_data, parse_variables_mapping from httprunner.parser import build_url, parse_data, parse_variables_mapping
from httprunner.response import ResponseObject from httprunner.response import ResponseObject
@@ -53,7 +54,9 @@ class HttpRunner(object):
step_data = StepData(name=step.name) step_data = StepData(name=step.name)
# parse # parse
prepare_upload_step(step, self.__project_meta.functions)
request_dict = step.request.dict() request_dict = step.request.dict()
request_dict.pop("upload", None)
parsed_request_dict = parse_data( parsed_request_dict = parse_data(
request_dict, step.variables, self.__project_meta.functions request_dict, step.variables, self.__project_meta.functions
) )
+4 -12
View File
@@ -13,12 +13,8 @@ class TestHttpRunner(unittest.TestCase):
) )
result = self.runner.get_summary() result = self.runner.get_summary()
self.assertTrue(result.success) self.assertTrue(result.success)
self.assertEqual( self.assertEqual(result.name, "request methods testcase with variables")
result.name, "request methods testcase with variables" self.assertEqual(result.step_datas[0].name, "get with params")
)
self.assertEqual(
result.step_datas[0].name, "get with params"
)
self.assertEqual(len(result.step_datas), 3) self.assertEqual(len(result.step_datas), 3)
def test_run_testcase_by_path_ref_testcase(self): def test_run_testcase_by_path_ref_testcase(self):
@@ -27,10 +23,6 @@ class TestHttpRunner(unittest.TestCase):
) )
result = self.runner.get_summary() result = self.runner.get_summary()
self.assertTrue(result.success) self.assertTrue(result.success)
self.assertEqual( self.assertEqual(result.name, "request methods testcase: reference testcase")
result.name, "request methods testcase: reference testcase" self.assertEqual(result.step_datas[0].name, "request with variables")
)
self.assertEqual(
result.step_datas[0].name, "request with variables"
)
self.assertEqual(len(result.step_datas), 1) self.assertEqual(len(result.step_datas), 1)
+3 -1
View File
@@ -56,6 +56,7 @@ class Request(BaseModel):
timeout: int = 120 timeout: int = 120
allow_redirects: bool = True allow_redirects: bool = True
verify: Verify = False verify: Verify = False
upload: Dict = {} # used for upload files
class TStep(BaseModel): class TStep(BaseModel):
@@ -107,7 +108,7 @@ class RequestData(BaseModel):
url: Url url: Url
headers: Headers = {} headers: Headers = {}
# TODO: add cookies # TODO: add cookies
body: Union[Text, Dict] = {} body: Union[Text, bytes, Dict, None] = {}
class ResponseData(BaseModel): class ResponseData(BaseModel):
@@ -137,6 +138,7 @@ class SessionData(BaseModel):
class StepData(BaseModel): class StepData(BaseModel):
"""teststep data, each step maybe corresponding to one request or one testcase""" """teststep data, each step maybe corresponding to one request or one testcase"""
success: bool = False success: bool = False
name: Text = "" # teststep name name: Text = "" # teststep name
data: Union[SessionData, List[SessionData]] = None data: Union[SessionData, List[SessionData]] = None