Permalink
Browse files

refactor hook mechanism:

1, remove EventHook;
2, setup_hooks: could reference request dict;
3, teardown_hooks: could reference Response object.
  • Loading branch information...
httprunner
httprunner committed May 10, 2018
1 parent 9bd0041 commit 2bb84b38745d004d336ed9867df5e63534b596bc
View
@@ -1,7 +1,7 @@
__title__ = 'HttpRunner'
__description__ = 'One-stop solution for HTTP(S) testing.'
__url__ = 'https://github.com/HttpRunner/HttpRunner'
__version__ = '1.4.2'
__version__ = '1.4.3'
__author__ = 'debugtalk'
__author_email__ = 'mail@debugtalk.com'
__license__ = 'MIT'
View
@@ -132,25 +132,25 @@ def endswith(check_value, expect_value):
""" built-in hooks
"""
def setup_hook_prepare_kwargs(method, url, kwargs):
if method == "POST":
content_type = kwargs.get("headers", {}).get("content-type")
if content_type and "data" in kwargs:
def setup_hook_prepare_kwargs(request):
if request["method"] == "POST":
content_type = request.get("headers", {}).get("content-type")
if content_type and "data" in request:
# if request content-type is application/json, request data should be dumped
if content_type.startswith("application/json") and isinstance(kwargs["data"], (dict, list)):
kwargs["data"] = json.dumps(kwargs["data"])
if content_type.startswith("application/json") and isinstance(request["data"], (dict, list)):
request["data"] = json.dumps(request["data"])
if isinstance(kwargs["data"], str):
kwargs["data"] = kwargs["data"].encode('utf-8')
if isinstance(request["data"], str):
request["data"] = request["data"].encode('utf-8')
def setup_hook_httpntlmauth(method, url, kwargs):
if "httpntlmauth" in kwargs:
def setup_hook_httpntlmauth(request):
if "httpntlmauth" in request:
from requests_ntlm import HttpNtlmAuth
auth_account = kwargs.pop("httpntlmauth")
kwargs["auth"] = HttpNtlmAuth(
auth_account = request.pop("httpntlmauth")
request["auth"] = HttpNtlmAuth(
auth_account["username"], auth_account["password"])
def teardown_hook_sleep_1_secs(resp_obj):
""" sleep 1 seconds after request
def sleep_N_secs(n_secs):
""" sleep n seconds
"""
time.sleep(1)
time.sleep(n_secs)
View

This file was deleted.

Oops, something went wrong.
View
@@ -5,7 +5,6 @@
from httprunner import exception, logger, response, utils
from httprunner.client import HttpSession
from httprunner.context import Context
from httprunner.events import EventHook
class Runner(object):
@@ -94,45 +93,10 @@ def _handle_skip_feature(self, testcase_dict):
if skip_reason:
raise SkipTest(skip_reason)
def _prepare_hooks_event(self, hooks):
if not hooks:
return None
event = EventHook()
for hook in hooks:
func = self.context.testcase_parser.get_bind_function(hook)
event += func
return event
def _call_setup_hooks(self, hooks, method, url, kwargs):
""" call hook functions before request
Listeners should take the following arguments:
* *method*: request method type, e.g. GET, POST, PUT
* *url*: URL that was called (or override name if it was used in the call to the client)
* *kwargs*: kwargs of request
"""
hooks.insert(0, "setup_hook_prepare_kwargs")
event = self._prepare_hooks_event(hooks)
if not event:
return
event.fire(method=method, url=url, kwargs=kwargs)
def _call_teardown_hooks(self, hooks, resp_obj):
""" call hook functions after request
Listeners should take the following arguments:
* *resp_obj*: response object
"""
event = self._prepare_hooks_event(hooks)
if not event:
return
event.fire(resp_obj=resp_obj)
def do_hook_actions(self, actions):
for action in actions:
logger.log_debug("call hook: {}".format(action))
self.context.eval_content(action)
def run_test(self, testcase_dict):
""" run single testcase.
@@ -161,7 +125,12 @@ def run_test(self, testcase_dict):
}
@return True or raise exception during test
"""
# check skip
self._handle_skip_feature(testcase_dict)
# prepare
parsed_request = self.init_config(testcase_dict, level="testcase")
self.context.bind_variables({"request": parsed_request})
try:
url = parsed_request.pop('url')
@@ -170,28 +139,36 @@ def run_test(self, testcase_dict):
except KeyError:
raise exception.ParamsError("URL or METHOD missed!")
self._handle_skip_feature(testcase_dict)
logger.log_info("{method} {url}".format(method=method, url=url))
logger.log_debug("request kwargs(raw): {kwargs}".format(kwargs=parsed_request))
extractors = testcase_dict.get("extract", []) or testcase_dict.get("extractors", [])
validators = testcase_dict.get("validate", []) or testcase_dict.get("validators", [])
# setup hooks
setup_hooks = testcase_dict.get("setup_hooks", [])
teardown_hooks = testcase_dict.get("teardown_hooks", [])
setup_hooks.insert(0, "${setup_hook_prepare_kwargs($request)}")
self.do_hook_actions(setup_hooks)
logger.log_info("{method} {url}".format(method=method, url=url))
logger.log_debug("request kwargs(raw): {kwargs}".format(kwargs=parsed_request))
self._call_setup_hooks(setup_hooks, method, url, parsed_request)
# request
resp = self.http_client_session.request(
method,
url,
name=group_name,
**parsed_request
)
self._call_teardown_hooks(teardown_hooks, resp)
resp_obj = response.ResponseObject(resp)
# teardown hooks
teardown_hooks = testcase_dict.get("teardown_hooks", [])
if teardown_hooks:
self.context.bind_variables({"response": resp})
self.do_hook_actions(teardown_hooks)
# extract
extractors = testcase_dict.get("extract", []) or testcase_dict.get("extractors", [])
resp_obj = response.ResponseObject(resp)
extracted_variables_mapping = resp_obj.extract_response(extractors)
self.context.bind_extracted_variables(extracted_variables_mapping)
# validate
validators = testcase_dict.get("validate", []) or testcase_dict.get("validators", [])
try:
self.context.validate(validators, resp_obj)
except (exception.ParamsError, exception.ResponseError, \
View
@@ -10,7 +10,7 @@
import re
from httprunner import exception, logger, utils
from httprunner.compat import OrderedDict, numeric_types
from httprunner.compat import OrderedDict, basestring, numeric_types
from httprunner.utils import FileUtils
variable_regexp = r"\$([\w_]+)"
@@ -75,14 +75,15 @@ def parse_function(content):
func(a=1, b=2) => {'func_name': 'func', 'args': [], 'kwargs': {'a': 1, 'b': 2}}
func(1, 2, a=3, b=4) => {'func_name': 'func', 'args': [1, 2], 'kwargs': {'a':3, 'b':4}}
"""
matched = function_regexp_compile.match(content)
if not matched:
raise exception.FunctionNotFound("{} not found!".format(content))
function_meta = {
"func_name": matched.group(1),
"args": [],
"kwargs": {}
}
matched = function_regexp_compile.match(content)
if not matched:
raise exception.ApiNotFound("{} not found!".format(content))
function_meta["func_name"] = matched.group(1)
args_str = matched.group(2).replace(" ", "")
if args_str == "":
@@ -597,6 +598,7 @@ def substitute_variables_with_mapping(content, mapping):
}
}
"""
# TODO: refactor type check
if isinstance(content, bool):
return content
@@ -903,17 +905,16 @@ def eval_content_with_bindings(self, content):
return evaluated_data
if isinstance(content, (numeric_types, type)):
return content
if isinstance(content, basestring):
# content is in string format here
content = content.strip()
# content is in string format here
content = content.strip()
# replace functions with evaluated value
# Notice: _eval_content_functions must be called before _eval_content_variables
content = self._eval_content_functions(content)
# replace functions with evaluated value
# Notice: _eval_content_functions must be called before _eval_content_variables
content = self._eval_content_functions(content)
# replace variables with binding value
content = self._eval_content_variables(content)
# replace variables with binding value
content = self._eval_content_variables(content)
return content
View
@@ -68,8 +68,16 @@ def gen_random_string(str_len):
random_string = ''.join(random_char_list)
return random_string
def setup_hook_add_kwargs(method, url, kwargs):
kwargs["key"] = "value"
def setup_hook_add_kwargs(request):
request["key"] = "value"
def setup_hook_remove_kwargs(method, url, kwargs):
kwargs.pop("key")
def setup_hook_remove_kwargs(request):
request.pop("key")
def teardown_hook_sleep_N_secs(response, n_secs):
""" sleep n seconds after request
"""
if response.status_code == 200:
time.sleep(0.1)
else:
time.sleep(n_secs)
View
@@ -9,8 +9,10 @@
url: /headers
method: GET
setup_hooks:
- setup_hook_add_kwargs
- setup_hook_remove_kwargs
- ${setup_hook_add_kwargs($request)}
- ${setup_hook_remove_kwargs($request)}
teardown_hooks:
- ${teardown_hook_sleep_N_secs($response, 1)}
validate:
- eq: ["status_code", 200]
- eq: [content.headers.Host, "127.0.0.1:3458"]
View
@@ -40,7 +40,9 @@ def test_request_without_base_url(self):
self.assertEqual(True, resp.json()['success'])
def test_prepare_kwargs_content_type_application_json_without_charset(self):
kwargs = {
request = {
"url": "/path",
"method": "POST",
"headers": {
"content-type": "application/json"
},
@@ -49,13 +51,15 @@ def test_prepare_kwargs_content_type_application_json_without_charset(self):
"b": 2
}
}
setup_hook_prepare_kwargs("POST", "/path", kwargs)
self.assertIsInstance(kwargs["data"], bytes)
self.assertIn(b'"a": 1', kwargs["data"])
self.assertIn(b'"b": 2', kwargs["data"])
setup_hook_prepare_kwargs(request)
self.assertIsInstance(request["data"], bytes)
self.assertIn(b'"a": 1', request["data"])
self.assertIn(b'"b": 2', request["data"])
def test_prepare_kwargs_content_type_application_json_charset_utf8(self):
kwargs = {
request = {
"url": "/path",
"method": "POST",
"headers": {
"content-type": "application/json; charset=utf-8"
},
@@ -64,5 +68,5 @@ def test_prepare_kwargs_content_type_application_json_charset_utf8(self):
"b": 2
}
}
setup_hook_prepare_kwargs("POST", "/path", kwargs)
self.assertIsInstance(kwargs["data"], bytes)
setup_hook_prepare_kwargs(request)
self.assertIsInstance(request["data"], bytes)
View
@@ -231,7 +231,7 @@ def test_get_parsed_request(self):
def test_exec_content_functions(self):
test_runner = runner.Runner()
content = "${teardown_hook_sleep_1_secs(1)}"
content = "${sleep_N_secs(1)}"
start_time = time.time()
test_runner.context.eval_content(content)
end_time = time.time()
Oops, something went wrong.

1 comment on commit 2bb84b3

@debugtalk

This comment has been minimized.

Collaborator

debugtalk commented on 2bb84b3 May 10, 2018

Please sign in to comment.