mirror of
https://github.com/httprunner/httprunner.git
synced 2026-09-06 07:57:27 +08:00
Merge branch 'master' into master
This commit is contained in:
+4
-1
@@ -2,13 +2,16 @@
|
|||||||
__pycache__
|
__pycache__
|
||||||
.DS_Store
|
.DS_Store
|
||||||
.vscode
|
.vscode
|
||||||
|
.idea
|
||||||
.pypirc
|
.pypirc
|
||||||
*/tmp/*
|
*/tmp/*
|
||||||
build/*
|
build/*
|
||||||
dist/*
|
dist/*
|
||||||
*.egg-info
|
*.egg-info
|
||||||
.python-version
|
.python-version
|
||||||
logs/%
|
logs
|
||||||
.coverage
|
.coverage
|
||||||
locustfile.py
|
locustfile.py
|
||||||
site/
|
site/
|
||||||
|
poetry.lock
|
||||||
|
reports
|
||||||
@@ -1,5 +1,16 @@
|
|||||||
# Release History
|
# Release History
|
||||||
|
|
||||||
|
## 2.2.6 (2019-09-18)
|
||||||
|
|
||||||
|
**Added**
|
||||||
|
|
||||||
|
- config variables support parsing from function
|
||||||
|
|
||||||
|
**Changed**
|
||||||
|
|
||||||
|
- remove unused import
|
||||||
|
- adjust code format
|
||||||
|
|
||||||
## 2.2.5 (2019-07-28)
|
## 2.2.5 (2019-07-28)
|
||||||
|
|
||||||
**Added**
|
**Added**
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
__version__ = "2.2.5"
|
__version__ = "2.2.6"
|
||||||
__description__ = "One-stop solution for HTTP(S) testing."
|
__description__ = "One-stop solution for HTTP(S) testing."
|
||||||
|
|
||||||
__all__ = ["__version__", "__description__"]
|
__all__ = ["__version__", "__description__"]
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
# encoding: utf-8
|
# encoding: utf-8
|
||||||
|
|
||||||
|
|
||||||
def main_hrun():
|
def main_hrun():
|
||||||
""" API test: parse command line options and run commands.
|
""" API test: parse command line options and run commands.
|
||||||
"""
|
"""
|
||||||
|
|||||||
+16
-7
@@ -17,10 +17,12 @@ try:
|
|||||||
except AttributeError:
|
except AttributeError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
## file loader
|
# file loader
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
|
|
||||||
def _check_format(file_path, content):
|
def _check_format(file_path, content):
|
||||||
""" check testcase format if valid
|
""" check testcase format if valid
|
||||||
"""
|
"""
|
||||||
@@ -239,9 +241,10 @@ def locate_file(start_path, file_name):
|
|||||||
|
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
## debugtalk.py module loader
|
# debugtalk.py module loader
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
|
|
||||||
def load_module_functions(module):
|
def load_module_functions(module):
|
||||||
""" load python module functions.
|
""" load python module functions.
|
||||||
|
|
||||||
@@ -290,9 +293,10 @@ def load_debugtalk_functions():
|
|||||||
|
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
## testcase loader
|
# testcase loader
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
|
|
||||||
project_mapping = {}
|
project_mapping = {}
|
||||||
tests_def_mapping = {
|
tests_def_mapping = {
|
||||||
"PWD": None,
|
"PWD": None,
|
||||||
@@ -720,14 +724,16 @@ def load_api_folder(api_folder_path):
|
|||||||
if isinstance(api_items, list):
|
if isinstance(api_items, list):
|
||||||
for api_item in api_items:
|
for api_item in api_items:
|
||||||
key, api_dict = api_item.popitem()
|
key, api_dict = api_item.popitem()
|
||||||
api_id = api_dict.get("id") or api_dict.get("def") or api_dict.get("name")
|
api_id = api_dict.get("id") or api_dict.get("def") \
|
||||||
|
or api_dict.get("name")
|
||||||
if key != "api" or not api_id:
|
if key != "api" or not api_id:
|
||||||
raise exceptions.ParamsError(
|
raise exceptions.ParamsError(
|
||||||
"Invalid API defined in {}".format(api_file_path))
|
"Invalid API defined in {}".format(api_file_path))
|
||||||
|
|
||||||
if api_id in api_definition_mapping:
|
if api_id in api_definition_mapping:
|
||||||
raise exceptions.ParamsError(
|
raise exceptions.ParamsError(
|
||||||
"Duplicated API ({}) defined in {}".format(api_id, api_file_path))
|
"Duplicated API ({}) defined in {}".format(
|
||||||
|
api_id, api_file_path))
|
||||||
else:
|
else:
|
||||||
api_definition_mapping[api_id] = api_dict
|
api_definition_mapping[api_id] = api_dict
|
||||||
|
|
||||||
@@ -745,7 +751,8 @@ def locate_debugtalk_py(start_path):
|
|||||||
""" locate debugtalk.py file
|
""" locate debugtalk.py file
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
start_path (str): start locating path, maybe testcase file path or directory path
|
start_path (str): start locating path,
|
||||||
|
maybe testcase file path or directory path
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
str: debugtalk.py file path, None if not found
|
str: debugtalk.py file path, None if not found
|
||||||
@@ -769,7 +776,8 @@ def load_project_tests(test_path, dot_env_path=None):
|
|||||||
dot_env_path (str): specified .env file path
|
dot_env_path (str): specified .env file path
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
dict: project loaded api/testcases definitions, environments and debugtalk.py functions.
|
dict: project loaded api/testcases definitions,
|
||||||
|
environments and debugtalk.py functions.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
# locate debugtalk.py file
|
# locate debugtalk.py file
|
||||||
@@ -876,6 +884,7 @@ def load_tests(path, dot_env_path=None):
|
|||||||
}
|
}
|
||||||
|
|
||||||
def __load_file_content(path):
|
def __load_file_content(path):
|
||||||
|
loaded_content = None
|
||||||
try:
|
try:
|
||||||
loaded_content = load_test_file(path)
|
loaded_content = load_test_file(path)
|
||||||
except exceptions.FileFormatError:
|
except exceptions.FileFormatError:
|
||||||
|
|||||||
+15
-3
@@ -2,11 +2,10 @@
|
|||||||
|
|
||||||
import ast
|
import ast
|
||||||
import builtins
|
import builtins
|
||||||
import os
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from httprunner import exceptions, utils, validator
|
from httprunner import exceptions, utils, validator
|
||||||
from httprunner.compat import basestring, builtin_str, numeric_types, str
|
from httprunner.compat import basestring, numeric_types, str
|
||||||
|
|
||||||
# use $$ to escape $ notation
|
# use $$ to escape $ notation
|
||||||
dolloar_regex_compile = re.compile(r"\$\$")
|
dolloar_regex_compile = re.compile(r"\$\$")
|
||||||
@@ -872,10 +871,23 @@ def __prepare_config(config, project_mapping, session_variables_set=None):
|
|||||||
"""
|
"""
|
||||||
# get config variables
|
# get config variables
|
||||||
raw_config_variables = config.pop("variables", {})
|
raw_config_variables = config.pop("variables", {})
|
||||||
raw_config_variables_mapping = utils.ensure_mapping_format(raw_config_variables)
|
|
||||||
override_variables = utils.deepcopy_dict(project_mapping.get("variables", {}))
|
override_variables = utils.deepcopy_dict(project_mapping.get("variables", {}))
|
||||||
functions = project_mapping.get("functions", {})
|
functions = project_mapping.get("functions", {})
|
||||||
|
|
||||||
|
if isinstance(raw_config_variables, basestring) and function_regex_compile.match(raw_config_variables):
|
||||||
|
# config variables are generated by calling function
|
||||||
|
# e.g.
|
||||||
|
# "config": {
|
||||||
|
# "name": "basic test with httpbin",
|
||||||
|
# "variables": "${gen_variables()}"
|
||||||
|
# }
|
||||||
|
raw_config_variables_mapping = parse_lazy_data(
|
||||||
|
prepare_lazy_data(raw_config_variables, functions_mapping=functions)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
raw_config_variables_mapping = utils.ensure_mapping_format(raw_config_variables)
|
||||||
|
|
||||||
# override config variables with passed in variables
|
# override config variables with passed in variables
|
||||||
raw_config_variables_mapping.update(override_variables)
|
raw_config_variables_mapping.update(override_variables)
|
||||||
|
|
||||||
|
|||||||
@@ -6,8 +6,6 @@ import jsonpath
|
|||||||
|
|
||||||
from httprunner import exceptions, logger, utils
|
from httprunner import exceptions, logger, utils
|
||||||
from httprunner.compat import OrderedDict, basestring, is_py2
|
from httprunner.compat import OrderedDict, basestring, is_py2
|
||||||
from requests.models import PreparedRequest
|
|
||||||
from requests.structures import CaseInsensitiveDict
|
|
||||||
|
|
||||||
|
|
||||||
text_extractor_regexp_compile = re.compile(r".*\(.*\).*")
|
text_extractor_regexp_compile = re.compile(r".*\(.*\).*")
|
||||||
@@ -112,7 +110,7 @@ class ResponseObject(object):
|
|||||||
"content.person.name.first_name"
|
"content.person.name.first_name"
|
||||||
|
|
||||||
"""
|
"""
|
||||||
# string.split(sep=None, maxsplit=-1) -> list of strings
|
# string.split(sep=None, maxsplit=1) -> list of strings
|
||||||
# e.g. "content.person.name" => ["content", "person.name"]
|
# e.g. "content.person.name" => ["content", "person.name"]
|
||||||
try:
|
try:
|
||||||
top_query, sub_query = field.split('.', 1)
|
top_query, sub_query = field.split('.', 1)
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "httprunner"
|
name = "httprunner"
|
||||||
version = "2.2.5"
|
version = "2.2.6"
|
||||||
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"
|
||||||
|
|||||||
@@ -111,3 +111,10 @@ def alter_response(response):
|
|||||||
def alter_response_error(response):
|
def alter_response_error(response):
|
||||||
# NameError
|
# NameError
|
||||||
not_defined_variable
|
not_defined_variable
|
||||||
|
|
||||||
|
|
||||||
|
def gen_variables():
|
||||||
|
return {
|
||||||
|
"var_a": 1,
|
||||||
|
"var_b": 2
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import os
|
import os
|
||||||
import re
|
|
||||||
import time
|
import time
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
@@ -561,7 +560,6 @@ class TestParserBasic(unittest.TestCase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def test_parse_data_functions(self):
|
def test_parse_data_functions(self):
|
||||||
import random, string
|
|
||||||
functions_mapping = {
|
functions_mapping = {
|
||||||
"gen_random_string": gen_random_string
|
"gen_random_string": gen_random_string
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -337,3 +337,46 @@ class TestRunner(ApiServerUnittest):
|
|||||||
parsed_testcase = parsed_testcases[0]
|
parsed_testcase = parsed_testcases[0]
|
||||||
test_runner = runner.Runner(parsed_testcase["config"])
|
test_runner = runner.Runner(parsed_testcase["config"])
|
||||||
test_runner.run_test(parsed_testcase["teststeps"][0])
|
test_runner.run_test(parsed_testcase["teststeps"][0])
|
||||||
|
|
||||||
|
def test_run_testcase_config_variables_parsed_from_function(self):
|
||||||
|
testcases = [
|
||||||
|
{
|
||||||
|
"config": {
|
||||||
|
"name": "basic test with httpbin",
|
||||||
|
"base_url": HTTPBIN_SERVER,
|
||||||
|
"variables": "${gen_variables()}"
|
||||||
|
},
|
||||||
|
"teststeps": [
|
||||||
|
{
|
||||||
|
"name": "modify request headers",
|
||||||
|
"base_url": HTTPBIN_SERVER,
|
||||||
|
"request": {
|
||||||
|
"url": "/anything",
|
||||||
|
"method": "POST",
|
||||||
|
"headers": {
|
||||||
|
"user_agent": "iOS/10.3",
|
||||||
|
"os_platform": "ios"
|
||||||
|
},
|
||||||
|
"data": "a=1&b=2"
|
||||||
|
},
|
||||||
|
"validate": [
|
||||||
|
{"check": "status_code", "expect": 200}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
tests_mapping = {
|
||||||
|
"project_mapping": {
|
||||||
|
"functions": self.debugtalk_functions
|
||||||
|
},
|
||||||
|
"testcases": testcases
|
||||||
|
}
|
||||||
|
parsed_testcases = parser.parse_tests(tests_mapping)
|
||||||
|
parsed_testcase = parsed_testcases[0]
|
||||||
|
test_runner = runner.Runner(parsed_testcase["config"])
|
||||||
|
test_runner.run_test(parsed_testcase["teststeps"][0])
|
||||||
|
test_variables_mapping = test_runner.session_context.test_variables_mapping
|
||||||
|
self.assertEqual(test_variables_mapping["var_a"], 1)
|
||||||
|
self.assertEqual(test_variables_mapping["var_b"], 2)
|
||||||
|
self.assertEqual(test_variables_mapping["request"]["data"], "a=1&b=2")
|
||||||
|
|||||||
Reference in New Issue
Block a user