Merge branch 'master' into master

This commit is contained in:
debugtalk
2019-09-19 00:29:42 +08:00
committed by GitHub
11 changed files with 102 additions and 20 deletions
+4 -1
View File
@@ -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
+11
View File
@@ -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 -1
View File
@@ -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__"]
+2 -1
View File
@@ -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.
""" """
@@ -11,7 +12,7 @@ def main_hrun():
from httprunner.compat import is_py2 from httprunner.compat import is_py2
from httprunner.validator import validate_json_file from httprunner.validator import validate_json_file
from httprunner.utils import (create_scaffold, get_python2_retire_msg, from httprunner.utils import (create_scaffold, get_python2_retire_msg,
prettify_json_file) prettify_json_file)
parser = argparse.ArgumentParser(description=__description__) parser = argparse.ArgumentParser(description=__description__)
parser.add_argument( parser.add_argument(
+16 -7
View File
@@ -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
View File
@@ -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)
+1 -3
View File
@@ -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
View File
@@ -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"
+7
View File
@@ -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
}
-2
View File
@@ -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
} }
+43
View File
@@ -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")