Merge pull request #923 from httprunner/v3

3.0.9 bugfix

- fix: ensure variables
- fix: handle exception for loaded invalid format test content
- change: do not capture exception for loading har file
This commit is contained in:
debugtalk
2020-06-07 15:11:53 +08:00
committed by GitHub
6 changed files with 101 additions and 34 deletions

View File

@@ -7,7 +7,7 @@ from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase
class TestCaseRequestWithFunctions(HttpRunner): class TestCaseRequestWithFunctions(HttpRunner):
config = ( config = (
Config("request with functions") Config("request with functions")
.variables(**{"foo1": "session_bar1", "var1": "testsuite_val1"}) .variables(**{"var1": "testsuite_val1", "foo1": "session_bar1"})
.base_url("https://postman-echo.com") .base_url("https://postman-echo.com")
.verify(False) .verify(False)
.export(*["session_foo2"]) .export(*["session_foo2"])

View File

@@ -16,7 +16,7 @@ from examples.postman_echo.request_methods.request_with_functions_test import (
class TestCaseRequestWithTestcaseReference(HttpRunner): class TestCaseRequestWithTestcaseReference(HttpRunner):
config = ( config = (
Config("request with referenced testcase") Config("request with referenced testcase")
.variables(**{"foo1": "session_bar1", "var2": "testsuite_val2"}) .variables(**{"var2": "testsuite_val2", "foo1": "session_bar1"})
.base_url("https://postman-echo.com") .base_url("https://postman-echo.com")
.verify(False) .verify(False)
) )

View File

@@ -3,15 +3,49 @@ This module handles compatibility issues between testcase format v2 and v3.
""" """
import os import os
import sys import sys
from typing import List, Dict, Text, Union from typing import List, Dict, Text, Union, Any
from loguru import logger from loguru import logger
from httprunner import exceptions from httprunner import exceptions
from httprunner.loader import load_project_meta from httprunner.loader import load_project_meta
from httprunner.parser import parse_data
from httprunner.utils import sort_dict_by_custom_order, ensure_file_path_valid from httprunner.utils import sort_dict_by_custom_order, ensure_file_path_valid
def convert_variables(
raw_variables: Union[Dict, List, Text], test_path: Text
) -> Dict[Text, Any]:
if isinstance(raw_variables, Dict):
return raw_variables
if isinstance(raw_variables, List):
# [{"var1": 1}, {"var2": 2}]
variables: Dict[Text, Any] = {}
for var_item in raw_variables:
if not isinstance(var_item, Dict) or len(var_item) != 1:
raise exceptions.TestCaseFormatError(
f"Invalid variables format: {raw_variables}"
)
variables.update(var_item)
return variables
elif isinstance(raw_variables, Text):
# get variables by function, e.g. ${get_variables()}
project_meta = load_project_meta(test_path)
variables = parse_data(raw_variables, {}, project_meta.functions)
return variables
else:
raise exceptions.TestCaseFormatError(
f"Invalid variables format: {raw_variables}"
)
def convert_jmespath(raw: Text) -> Text: def convert_jmespath(raw: Text) -> Text:
# content.xx/json.xx => body.xx # content.xx/json.xx => body.xx
if raw.startswith("content"): if raw.startswith("content"):

View File

@@ -6,7 +6,6 @@ from urllib.parse import unquote
import yaml import yaml
from loguru import logger from loguru import logger
from sentry_sdk import capture_exception
def load_har_log_entries(file_path): def load_har_log_entries(file_path):
@@ -33,9 +32,11 @@ def load_har_log_entries(file_path):
try: try:
content_json = json.loads(f.read()) content_json = json.loads(f.read())
return content_json["log"]["entries"] return content_json["log"]["entries"]
except (KeyError, TypeError, JSONDecodeError) as ex: except (TypeError, JSONDecodeError) as ex:
capture_exception(ex) logger.error(f"failed to load HAR file {file_path}: {ex}")
logger.error("HAR file content error: {}".format(file_path)) sys.exit(1)
except KeyError:
logger.error(f"log entries not found in HAR file: {content_json}")
sys.exit(1) sys.exit(1)
@@ -53,7 +54,7 @@ def x_www_form_urlencoded(post_data):
""" """
if isinstance(post_data, dict): if isinstance(post_data, dict):
return "&".join( return "&".join(
[u"{}={}".format(key, value) for key, value in post_data.items()] ["{}={}".format(key, value) for key, value in post_data.items()]
) )
else: else:
return post_data return post_data

View File

@@ -1,5 +1,6 @@
import os import os
import subprocess import subprocess
import sys
from shutil import copyfile from shutil import copyfile
from typing import Text, List, Tuple, Dict, Set, NoReturn from typing import Text, List, Tuple, Dict, Set, NoReturn
@@ -8,7 +9,11 @@ from loguru import logger
from sentry_sdk import capture_exception from sentry_sdk import capture_exception
from httprunner import exceptions, __version__ from httprunner import exceptions, __version__
from httprunner.compat import ensure_testcase_v3_api, ensure_testcase_v3 from httprunner.compat import (
ensure_testcase_v3_api,
ensure_testcase_v3,
convert_variables,
)
from httprunner.loader import ( from httprunner.loader import (
load_folder_files, load_folder_files,
load_test_file, load_test_file,
@@ -284,15 +289,9 @@ def make_testcase(testcase: Dict, dir_path: Text = None) -> Text:
config = testcase["config"] config = testcase["config"]
config["path"] = __ensure_cwd_relative(testcase_python_path) config["path"] = __ensure_cwd_relative(testcase_python_path)
config["variables"] = convert_variables(
# parse config variables config.get("variables", {}), testcase_abs_path
config.setdefault("variables", {}) )
if isinstance(config["variables"], Text):
# get variables by function, e.g. ${get_variables()}
project_meta = load_project_meta(testcase_abs_path)
config["variables"] = parse_data(
config["variables"], {}, project_meta.functions
)
# prepare reference testcase # prepare reference testcase
imports_list = [] imports_list = []
@@ -356,14 +355,9 @@ def make_testsuite(testsuite: Dict) -> NoReturn:
testsuite_config = testsuite["config"] testsuite_config = testsuite["config"]
testsuite_path = testsuite_config["path"] testsuite_path = testsuite_config["path"]
testsuite_variables = convert_variables(
testsuite_variables = testsuite_config.get("variables", {}) testsuite_config.get("variables", {}), testsuite_path
if isinstance(testsuite_variables, Text): )
# get variables by function, e.g. ${get_variables()}
project_meta = load_project_meta(testsuite_path)
testsuite_variables = parse_data(
testsuite_variables, {}, project_meta.functions
)
logger.info(f"start to make testsuite: {testsuite_path}") logger.info(f"start to make testsuite: {testsuite_path}")
@@ -391,9 +385,11 @@ def make_testsuite(testsuite: Dict) -> NoReturn:
if "verify" in testsuite_config: if "verify" in testsuite_config:
testcase_dict["config"]["verify"] = testsuite_config["verify"] testcase_dict["config"]["verify"] = testsuite_config["verify"]
# override variables # override variables
testcase_dict["config"].setdefault("variables", {}) testcase_variables = convert_variables(
testcase_dict["config"]["variables"].update(testcase.get("variables", {})) testcase.get("variables", {}), testcase_path
testcase_dict["config"]["variables"].update(testsuite_variables) )
testcase_variables.update(testsuite_variables)
testcase_dict["config"]["variables"] = testcase_variables
# make testcase # make testcase
testcase_pytest_path = make_testcase(testcase_dict, testsuite_dir) testcase_pytest_path = make_testcase(testcase_dict, testsuite_dir)
@@ -428,12 +424,19 @@ def __make(tests_path: Text) -> NoReturn:
logger.warning(ex) logger.warning(ex)
continue continue
if not isinstance(test_content, Dict):
raise exceptions.FileFormatError(
f"test content not in dict format: {test_content}"
)
# api in v2 format, convert to v3 testcase # api in v2 format, convert to v3 testcase
if "request" in test_content: if "request" in test_content and "name" in test_content:
test_content = ensure_testcase_v3_api(test_content) test_content = ensure_testcase_v3_api(test_content)
if not (isinstance(test_content, Dict) and "config" in test_content): if "config" not in test_content:
raise exceptions.FileFormatError("Invalid testcase/testsuite v2/v3 format!") raise exceptions.FileFormatError(
f"miss config part in testcase/testsuite: {test_content}"
)
test_content.setdefault("config", {})["path"] = test_file test_content.setdefault("config", {})["path"] = test_file
@@ -465,7 +468,12 @@ def main_make(tests_paths: List[Text]) -> List[Text]:
if not os.path.isabs(tests_path): if not os.path.isabs(tests_path):
tests_path = os.path.join(os.getcwd(), tests_path) tests_path = os.path.join(os.getcwd(), tests_path)
__make(tests_path) try:
__make(tests_path)
except exceptions.MyBaseError as ex:
logger.error(ex)
sys.exit(1)
__ensure_project_meta_files(tests_path) __ensure_project_meta_files(tests_path)
# format pytest files # format pytest files

View File

@@ -1,10 +1,34 @@
import os import os
import unittest import unittest
from httprunner import compat from httprunner import compat, exceptions
from httprunner.compat import convert_variables
class TestCompat(unittest.TestCase): class TestCompat(unittest.TestCase):
def test_convert_variables(self):
raw_variables = [{"var1": 1}, {"var2": "val2"}]
self.assertEqual(
convert_variables(raw_variables, "tests/data/a-b.c/1.yml"),
{"var1": 1, "var2": "val2"},
)
raw_variables = {"var1": 1, "var2": "val2"}
self.assertEqual(
convert_variables(raw_variables, "tests/data/a-b.c/1.yml"),
{"var1": 1, "var2": "val2"},
)
raw_variables = "${get_variables()}"
self.assertEqual(
convert_variables(raw_variables, "tests/data/a-b.c/1.yml"),
{"foo1": "session_bar1"},
)
with self.assertRaises(exceptions.TestCaseFormatError):
raw_variables = [{"var1": 1}, {"var2": "val2", "var3": 3}]
convert_variables(raw_variables, "tests/data/a-b.c/1.yml")
with self.assertRaises(exceptions.TestCaseFormatError):
convert_variables(None, "tests/data/a-b.c/1.yml")
def test_convert_jmespath(self): def test_convert_jmespath(self):
self.assertEqual(compat.convert_jmespath("content.abc"), "body.abc") self.assertEqual(compat.convert_jmespath("content.abc"), "body.abc")