diff --git a/httprunner/__about__.py b/httprunner/__about__.py index 9fc5c801..0cde284c 100644 --- a/httprunner/__about__.py +++ b/httprunner/__about__.py @@ -1,7 +1,7 @@ __title__ = 'HttpRunner' __description__ = 'One-stop solution for HTTP(S) testing.' __url__ = 'https://github.com/HttpRunner/HttpRunner' -__version__ = '1.5.6' +__version__ = '1.5.7' __author__ = 'debugtalk' __author_email__ = 'mail@debugtalk.com' __license__ = 'MIT' diff --git a/httprunner/built_in.py b/httprunner/built_in.py index 32f94aad..e2fb0e69 100644 --- a/httprunner/built_in.py +++ b/httprunner/built_in.py @@ -13,7 +13,7 @@ import string import time from httprunner.compat import basestring, builtin_str, integer_types, str -from httprunner.exception import ParamsError +from httprunner.exceptions import ParamsError from requests_toolbelt import MultipartEncoder diff --git a/httprunner/client.py b/httprunner/client.py index 9b2ecfd5..0f9e4d9a 100644 --- a/httprunner/client.py +++ b/httprunner/client.py @@ -6,7 +6,7 @@ import time import requests import urllib3 from httprunner import logger -from httprunner.exception import ParamsError +from httprunner.exceptions import ParamsError from requests import Request, Response from requests.exceptions import (InvalidSchema, InvalidURL, MissingSchema, RequestException) @@ -115,7 +115,7 @@ class HttpSession(requests.Session): def log_print(request_response): msg = "\n================== {} details ==================\n".format(request_response) for key, value in self.meta_data[request_response].items(): - msg += "{:<16} : {}\n".format(key, value) + msg += "{:<16} : {}\n".format(key, repr(value)) logger.log_debug(msg) # record original request info diff --git a/httprunner/compat.py b/httprunner/compat.py index 3753affc..ae88f4c9 100644 --- a/httprunner/compat.py +++ b/httprunner/compat.py @@ -8,6 +8,11 @@ This module handles import compatibility issues between Python 2 and Python 3. """ +try: + import simplejson as json +except ImportError: + import json + import sys # ------- @@ -23,15 +28,16 @@ is_py2 = (_ver[0] == 2) #: Python 3.x? is_py3 = (_ver[0] == 3) -try: - import simplejson as json -except ImportError: - import json # --------- # Specifics # --------- +try: + JSONDecodeError = json.JSONDecodeError +except AttributeError: + JSONDecodeError = ValueError + if is_py2: from urllib3.packages.ordered_dict import OrderedDict @@ -42,6 +48,8 @@ if is_py2: numeric_types = (int, long, float) integer_types = (int, long) + FileNotFoundError = IOError + elif is_py3: from collections import OrderedDict @@ -51,3 +59,5 @@ elif is_py3: basestring = (str, bytes) numeric_types = (int, float) integer_types = (int,) + + FileNotFoundError = FileNotFoundError diff --git a/httprunner/context.py b/httprunner/context.py index 9decb7ca..59734b29 100644 --- a/httprunner/context.py +++ b/httprunner/context.py @@ -5,7 +5,7 @@ import os import re import sys -from httprunner import exception, testcase, utils +from httprunner import exceptions, logger, testcase, utils from httprunner.compat import OrderedDict @@ -17,6 +17,7 @@ class Context(object): self.testset_shared_variables_mapping = OrderedDict() self.testcase_variables_mapping = OrderedDict() self.testcase_parser = testcase.TestcaseParser() + self.evaluated_validators = [] self.init_context() def init_context(self, level='testset'): @@ -201,13 +202,8 @@ class Context(object): # format 1/2/3 check_value = self.eval_content(check_item) else: - try: - # format 4/5 - check_value = resp_obj.extract_field(check_item) - except exception.ParseResponseError: - msg = "failed to extract check item from response!\n" - msg += "response content: {}".format(resp_obj.content) - raise exception.ParseResponseError(msg) + # format 4/5 + check_value = resp_obj.extract_field(check_item) validator["check_value"] = check_value @@ -227,7 +223,7 @@ class Context(object): validate_func = self.testcase_parser.get_bind_function(comparator) if not validate_func: - raise exception.FunctionNotFound("comparator not found: {}".format(comparator)) + raise exceptions.FunctionNotFound("comparator not found: {}".format(comparator)) check_item = validator_dict["check"] check_value = validator_dict["check_value"] @@ -235,34 +231,56 @@ class Context(object): if (check_value is None or expect_value is None) \ and comparator not in ["is", "eq", "equals", "=="]: - raise exception.ParamsError("Null value can only be compared with comparator: eq/equals/==") + raise exceptions.ParamsError("Null value can only be compared with comparator: eq/equals/==") + + validate_msg = "validate: {} {} {}({})".format( + check_item, + comparator, + expect_value, + type(expect_value).__name__ + ) try: - validator_dict["check_result"] = "passed" - validate_func(validator_dict["check_value"], validator_dict["expect"]) + validator_dict["check_result"] = "pass" + validate_func(check_value, expect_value) + validate_msg += "\t==> pass" + logger.log_debug(validate_msg) except (AssertionError, TypeError): - err_msg = "\n" + "\n".join([ - "\tcheck item name: %s;" % check_item, - "\tcheck item value: %s (%s);" % (check_value, type(check_value).__name__), - "\tcomparator: %s;" % comparator, - "\texpected value: %s (%s)." % (expect_value, type(expect_value).__name__) - ]) - validator_dict["check_result"] = "failed" - raise exception.ValidationError(err_msg) + validate_msg += "\t==> fail" + validate_msg += "\n{}({}) {} {}({})".format( + check_value, + type(check_value).__name__, + comparator, + expect_value, + type(expect_value).__name__ + ) + logger.log_error(validate_msg) + validator_dict["check_result"] = "fail" + raise exceptions.ValidationFailure(validate_msg) - def eval_validators(self, validators, resp_obj): - """ evaluate validators with context variable mapping. + def validate(self, validators, resp_obj): + """ make validations """ - return [ - self.eval_check_item( + if not validators: + return + + logger.log_info("start to validate.") + self.evaluated_validators = [] + validate_pass = True + + for validator in validators: + # evaluate validators with context variable mapping. + evaluated_validator = self.eval_check_item( testcase.parse_validator(validator), resp_obj ) - for validator in validators - ] - def validate(self, validators): - """ make validations - """ - for validator_dict in validators: - self.do_validation(validator_dict) + try: + self.do_validation(evaluated_validator) + except exceptions.ValidationFailure: + validate_pass = False + + self.evaluated_validators.append(evaluated_validator) + + if not validate_pass: + raise exceptions.ValidationFailure diff --git a/httprunner/exception.py b/httprunner/exceptions.py similarity index 53% rename from httprunner/exception.py rename to httprunner/exceptions.py index 4bcef4d8..29b743d7 100644 --- a/httprunner/exception.py +++ b/httprunner/exceptions.py @@ -1,16 +1,24 @@ # encoding: utf-8 -import json +from httprunner.compat import JSONDecodeError, FileNotFoundError -try: - FileNotFoundError = FileNotFoundError -except NameError: - FileNotFoundError = IOError +""" failure type exceptions + these exceptions will mark test as failure +""" -try: - JSONDecodeError = json.decoder.JSONDecodeError -except AttributeError: - JSONDecodeError = ValueError +class MyBaseFailure(BaseException): + pass + +class ValidationFailure(MyBaseFailure): + pass + +class ExtractFailure(MyBaseFailure): + pass + + +""" error type exceptions + these exceptions will mark test as error +""" class MyBaseError(BaseException): pass @@ -21,18 +29,12 @@ class FileFormatError(MyBaseError): class ParamsError(MyBaseError): pass -class ResponseError(MyBaseError): - pass - -class ParseResponseError(MyBaseError): - pass - -class ValidationError(MyBaseError): - pass - class NotFoundError(MyBaseError): pass +class FileNotFound(FileNotFoundError, NotFoundError): + pass + class FunctionNotFound(NotFoundError): pass diff --git a/httprunner/report.py b/httprunner/report.py index 7cb6b172..73381570 100644 --- a/httprunner/report.py +++ b/httprunner/report.py @@ -90,7 +90,9 @@ def render_html_report(summary, html_report_name=None, html_report_template=None if not os.path.isdir(report_dir_path): os.makedirs(report_dir_path) - for suite_summary in summary["details"]: + for index, suite_summary in enumerate(summary["details"]): + if not suite_summary.get("name"): + suite_summary["name"] = "test suite {}".format(index) for record in suite_summary.get("records"): meta_data = record['meta_data'] stringify_data(meta_data, 'request') diff --git a/httprunner/response.py b/httprunner/response.py index 9a2a3b89..d562769a 100644 --- a/httprunner/response.py +++ b/httprunner/response.py @@ -3,7 +3,7 @@ import json import re -from httprunner import exception, logger, testcase, utils +from httprunner import exceptions, logger, testcase, utils from httprunner.compat import OrderedDict, basestring from requests.structures import CaseInsensitiveDict from requests.models import PreparedRequest @@ -31,7 +31,7 @@ class ResponseObject(object): except AttributeError: err_msg = "ResponseObject does not have attribute: {}".format(key) logger.log_error(err_msg) - raise exception.ParamsError(err_msg) + raise exceptions.ParamsError(err_msg) def _extract_field_with_regex(self, field): """ extract field from response content with regex. @@ -44,11 +44,10 @@ class ResponseObject(object): """ matched = re.search(field, self.text) if not matched: - err_msg = u"Failed to extract data with regex!\n" - err_msg += u"response content: {}\n".format(self.content) - err_msg += u"regex: {}\n".format(field) + err_msg = u"Failed to extract data with regex! => {}\n".format(field) + err_msg += u"response body: {}\n".format(self.text) logger.log_error(err_msg) - raise exception.ParamsError(err_msg) + raise exceptions.ExtractFailure(err_msg) return matched.group(1) @@ -63,92 +62,120 @@ class ResponseObject(object): "headers.content-type" "content.person.name.first_name" """ + # string.split(sep=None, maxsplit=-1) -> list of strings + # e.g. "content.person.name" => ["content", "person.name"] try: - # string.split(sep=None, maxsplit=-1) -> list of strings - # e.g. "content.person.name" => ["content", "person.name"] - try: - top_query, sub_query = field.split('.', 1) - except ValueError: - top_query = field - sub_query = None - - if top_query == "cookies": - cookies = self.cookies - try: - return cookies[sub_query] - except KeyError: - err_msg = u"Failed to extract attribute from cookies!\n" - err_msg += u"cookies: {}\n".format(cookies) - err_msg += u"attribute: {}".format(sub_query) - logger.log_error(err_msg) - raise exception.ParamsError(err_msg) - elif top_query == "elapsed": - if sub_query in ["days", "seconds", "microseconds"]: - return getattr(self.elapsed, sub_query) - elif sub_query == "total_seconds": - return self.elapsed.total_seconds() - else: - err_msg = "{}: {} is not valid timedelta attribute.\n".format(field, sub_query) - err_msg += "elapsed only support attributes: days, seconds, microseconds, total_seconds.\n" - logger.log_error(err_msg) - raise exception.ParamsError(err_msg) - - try: - top_query_content = getattr(self, top_query) - except AttributeError: - err_msg = u"Failed to extract attribute from response object: resp_obj.{}".format(top_query) - logger.log_error(err_msg) - raise exception.ParamsError(err_msg) + top_query, sub_query = field.split('.', 1) + except ValueError: + top_query = field + sub_query = None + # status_code + if top_query in ["status_code", "encoding", "ok", "reason", "url"]: if sub_query: - if not isinstance(top_query_content, (dict, CaseInsensitiveDict, list)): - try: - # TODO: remove compatibility for content, text - if isinstance(top_query_content, bytes): - top_query_content = top_query_content.decode("utf-8") + # status_code.XX + err_msg = u"Failed to extract: {}\n".format(field) + logger.log_error(err_msg) + raise exceptions.ParamsError(err_msg) - if isinstance(top_query_content, PreparedRequest): - top_query_content = top_query_content.__dict__ - else: - top_query_content = json.loads(top_query_content) - except json.decoder.JSONDecodeError: - err_msg = u"Failed to extract data with delimiter!\n" - err_msg += u"response content: {}\n".format(self.content) - err_msg += u"regex: {}\n".format(field) - logger.log_error(err_msg) - raise exception.ParamsError(err_msg) + return getattr(self, top_query) - # e.g. key: resp_headers_content_type, sub_query = "content-type" - return utils.query_json(top_query_content, sub_query) + # cookies + elif top_query == "cookies": + cookies = self.cookies.get_dict() + if not sub_query: + # extract cookies + return cookies + + try: + return cookies[sub_query] + except KeyError: + err_msg = u"Failed to extract cookie! => {}\n".format(field) + err_msg += u"response cookies: {}\n".format(cookies) + logger.log_error(err_msg) + raise exceptions.ExtractFailure(err_msg) + + # elapsed + elif top_query == "elapsed": + available_attributes = u"available attributes: days, seconds, microseconds, total_seconds" + if not sub_query: + err_msg = u"elapsed is datetime.timedelta instance, attribute should also be specified!\n" + err_msg += available_attributes + logger.log_error(err_msg) + raise exceptions.ParamsError(err_msg) + elif sub_query in ["days", "seconds", "microseconds"]: + return getattr(self.elapsed, sub_query) + elif sub_query == "total_seconds": + return self.elapsed.total_seconds() else: - # e.g. key: resp_status_code, resp_content - return top_query_content + err_msg = "{} is not valid datetime.timedelta attribute.\n".format(sub_query) + err_msg += available_attributes + logger.log_error(err_msg) + raise exceptions.ParamsError(err_msg) - except AttributeError: - err_msg = u"Failed to extract value from response!\n" - err_msg += u"response content: {}\n".format(self.content) - err_msg += u"extract field: {}\n".format(field) + # headers + elif top_query == "headers": + headers = self.headers + if not sub_query: + # extract headers + return headers + + try: + return headers[sub_query] + except KeyError: + err_msg = u"Failed to extract header! => {}\n".format(field) + err_msg += u"response headers: {}\n".format(headers) + logger.log_error(err_msg) + raise exceptions.ExtractFailure(err_msg) + + # response body + elif top_query in ["content", "text", "json"]: + try: + body = self.json + except exceptions.JSONDecodeError: + body = self.text + + if not sub_query: + # extract response body + return body + + if isinstance(body, (dict, list)): + # content = {"xxx": 123}, content.xxx + return utils.query_json(body, sub_query) + elif sub_query.isdigit(): + # content = "abcdefg", content.3 => d + return utils.query_json(body, sub_query) + else: + # content = "abcdefg", content.xxx + err_msg = u"Failed to extract attribute from response body! => {}\n".format(field) + err_msg += u"response body: {}\n".format(body) + logger.log_error(err_msg) + raise exceptions.ExtractFailure(err_msg) + + # others + else: + err_msg = u"Failed to extract attribute from response! => {}\n".format(field) + err_msg += u"available response attributes: status_code, cookies, elapsed, headers, content, text, json, encoding, ok, reason, url." logger.log_error(err_msg) - raise exception.ParamsError(err_msg) + raise exceptions.ParamsError(err_msg) def extract_field(self, field): """ extract value from requests.Response. """ - msg = "extract field: {}".format(field) + if not isinstance(field, basestring): + err_msg = u"Invalid extractor! => {}\n".format(field) + logger.log_error(err_msg) + raise exceptions.ParamsError(err_msg) - try: - if text_extractor_regexp_compile.match(field): - value = self._extract_field_with_regex(field) - else: - value = self._extract_field_with_delimiter(field) + msg = "extract: {}".format(field) - msg += "\t=> {}".format(value) - logger.log_debug(msg) + if text_extractor_regexp_compile.match(field): + value = self._extract_field_with_regex(field) + else: + value = self._extract_field_with_delimiter(field) - # TODO: unify ParseResponseError type - except (exception.ParseResponseError, TypeError): - logger.log_error("failed to extract field: {}".format(field)) - raise + msg += "\t=> {}".format(value) + logger.log_debug(msg) return value @@ -171,9 +198,6 @@ class ResponseObject(object): extract_binds_order_dict = utils.convert_to_order_dict(extractors) for key, field in extract_binds_order_dict.items(): - if not isinstance(field, basestring): - raise exception.ParamsError("invalid extractors in testcase!") - extracted_variables_mapping[key] = self.extract_field(field) return extracted_variables_mapping diff --git a/httprunner/runner.py b/httprunner/runner.py index fbf8031f..0174bb9c 100644 --- a/httprunner/runner.py +++ b/httprunner/runner.py @@ -2,7 +2,7 @@ from unittest.case import SkipTest -from httprunner import exception, logger, response, utils +from httprunner import exceptions, logger, response, utils from httprunner.client import HttpSession from httprunner.context import Context @@ -11,7 +11,6 @@ class Runner(object): def __init__(self, config_dict=None, http_client_session=None): self.http_client_session = http_client_session - self.evaluated_validators = [] self.context = Context() config_dict = config_dict or {} @@ -155,7 +154,15 @@ class Runner(object): method = parsed_request.pop('method') group_name = parsed_request.pop("group", None) except KeyError: - raise exception.ParamsError("URL or METHOD missed!") + raise exceptions.ParamsError("URL or METHOD missed!") + + # TODO: move method validation to json schema + valid_methods = ["GET", "HEAD", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"] + if method.upper() not in valid_methods: + err_msg = u"Invalid HTTP method! => {}\n".format(method) + err_msg += "Available HTTP methods: {}".format("/".join(valid_methods)) + logger.log_error(err_msg) + raise exceptions.ParamsError(err_msg) logger.log_info("{method} {url}".format(method=method, url=url)) logger.log_debug("request kwargs(raw): {kwargs}".format(kwargs=parsed_request)) @@ -183,22 +190,21 @@ class Runner(object): # validate validators = testcase_dict.get("validate", []) or testcase_dict.get("validators", []) try: - self.evaluated_validators = self.context.eval_validators(validators, resp_obj) - self.context.validate(self.evaluated_validators) - except (exception.ParamsError, exception.ResponseError, \ - exception.ValidationError, exception.ParseResponseError): + self.context.validate(validators, resp_obj) + except (exceptions.ParamsError, \ + exceptions.ValidationFailure, exceptions.ExtractFailure): # log request err_req_msg = "request: \n" err_req_msg += "headers: {}\n".format(parsed_request.pop("headers", {})) for k, v in parsed_request.items(): - err_req_msg += "{}: {}\n".format(k, v) + err_req_msg += "{}: {}\n".format(k, repr(v)) logger.log_error(err_req_msg) # log response err_resp_msg = "response: \n" err_resp_msg += "status_code: {}\n".format(resp_obj.status_code) err_resp_msg += "headers: {}\n".format(resp_obj.headers) - err_resp_msg += "content: {}\n".format(resp_obj.content) + err_resp_msg += "body: {}\n".format(repr(resp_obj.text)) logger.log_error(err_resp_msg) raise diff --git a/httprunner/task.py b/httprunner/task.py index c65a7809..437ae6d3 100644 --- a/httprunner/task.py +++ b/httprunner/task.py @@ -4,7 +4,7 @@ import copy import sys import unittest -from httprunner import exception, logger, runner, testcase, utils +from httprunner import exceptions, logger, runner, testcase, utils from httprunner.compat import is_py3 from httprunner.report import (HtmlTestResult, get_platform, get_summary, render_html_report) @@ -25,10 +25,12 @@ class TestCase(unittest.TestCase): """ try: self.test_runner.run_test(self.testcase_dict) + except exceptions.MyBaseFailure as ex: + self.fail(repr(ex)) finally: if hasattr(self.test_runner.http_client_session, "meta_data"): self.meta_data = self.test_runner.http_client_session.meta_data - self.meta_data["validators"] = self.test_runner.evaluated_validators + self.meta_data["validators"] = self.test_runner.context.evaluated_validators self.test_runner.http_client_session.init_meta_data() @@ -106,7 +108,7 @@ class TestSuite(unittest.TestSuite): self.testcase_parser.update_binded_variables(variables) try: testcase_name = self.testcase_parser.eval_content_with_bindings(testcase_dict["name"]) - except (AssertionError, exception.ParamsError): + except (AssertionError, exceptions.ParamsError): logger.log_warning("failed to eval testcase name: {}".format(testcase_dict["name"])) testcase_name = testcase_dict["name"] self.test_runner_list.append((test_runner, variables)) @@ -189,7 +191,7 @@ def init_test_suites(path_or_testsets, mapping=None, http_client_session=None): mapping = mapping or {} if not testsets: - raise exception.TestcaseNotFound + raise exceptions.TestcaseNotFound if isinstance(testsets, dict): testsets = [testsets] @@ -236,7 +238,7 @@ class HttpRunner(object): """ try: test_suite_list = init_test_suites(path_or_testsets, mapping) - except exception.TestcaseNotFound: + except exceptions.TestcaseNotFound: logger.log_error("Testcases not found in {}".format(path_or_testsets)) sys.exit(1) @@ -301,7 +303,7 @@ class LocustTask(object): for test in test_suite: try: test.runTest() - except exception.MyBaseError as ex: + except exceptions.MyBaseError as ex: from locust.events import request_failure request_failure.fire( request_type=test.testcase_dict.get("request", {}).get("method"), diff --git a/httprunner/templates/default_report_template.html b/httprunner/templates/default_report_template.html index 4c7b4441..78d36986 100644 --- a/httprunner/templates/default_report_template.html +++ b/httprunner/templates/default_report_template.html @@ -187,69 +187,91 @@ {% for test_suite_summary in details %} {% set suite_index = loop.index %}

{{test_suite_summary.name}}

- - - - - - - - - - - - - - - - - - - - {% for record in test_suite_summary.records %} - {% set record_index = "{}_{}".format(suite_index, loop.index) %} - - - - + + {% endfor %} +
base_url{{test_suite_summary.base_url}} - parameters & output -
- -
- -
TOTAL: {{test_suite_summary.stat.testsRun}}SUCCESS: {{test_suite_summary.stat.successes}}FAILED: {{test_suite_summary.stat.failures}}ERROR: {{test_suite_summary.stat.errors}}SKIPPED: {{test_suite_summary.stat.skipped}}
StatusNameResponse TimeDetail
{{record.status}} - {{record.name}}{{ record.meta_data.response.response_time_ms }} ms - - log -
{% endfor %} \ No newline at end of file diff --git a/httprunner/testcase.py b/httprunner/testcase.py index e25eda8b..b7a52a2e 100644 --- a/httprunner/testcase.py +++ b/httprunner/testcase.py @@ -9,8 +9,9 @@ import os import random import re -from httprunner import exception, logger, utils -from httprunner.compat import OrderedDict, basestring, numeric_types +from httprunner import exceptions, logger, utils +from httprunner.compat import (OrderedDict, basestring, builtin_str, + numeric_types, str) from httprunner.utils import FileUtils variable_regexp = r"\$([\w_]+)" @@ -77,7 +78,7 @@ def parse_function(content): """ matched = function_regexp_compile.match(content) if not matched: - raise exception.FunctionNotFound("{} not found!".format(content)) + raise exceptions.FunctionNotFound("{} not found!".format(content)) function_meta = { "func_name": matched.group(1), @@ -126,7 +127,7 @@ class TestcaseLoader(object): for suite_file in FileUtils.load_folder_files(suite_def_folder): suite = TestcaseLoader.load_test_file(suite_file) if "def" not in suite["config"]: - raise exception.ParamsError("def missed in suite file: {}!".format(suite_file)) + raise exceptions.ParamsError("def missed in suite file: {}!".format(suite_file)) call_func = suite["config"]["def"] function_meta = parse_function(call_func) @@ -156,15 +157,15 @@ class TestcaseLoader(object): """ api_items = FileUtils.load_file(file_path) if not isinstance(api_items, list): - raise exception.FileFormatError("API format error: {}".format(file_path)) + raise exceptions.FileFormatError("API format error: {}".format(file_path)) for api_item in api_items: if not isinstance(api_item, dict) or len(api_item) != 1: - raise exception.FileFormatError("API format error: {}".format(file_path)) + raise exceptions.FileFormatError("API format error: {}".format(file_path)) key, api_dict = api_item.popitem() if key != "api" or not isinstance(api_dict, dict) or "def" not in api_dict: - raise exception.FileFormatError("API format error: {}".format(file_path)) + raise exceptions.FileFormatError("API format error: {}".format(file_path)) api_def = api_dict.pop("def") function_meta = parse_function(api_def) @@ -218,11 +219,11 @@ class TestcaseLoader(object): } for item in FileUtils.load_file(file_path): if not isinstance(item, dict) or len(item) != 1: - raise exception.FileFormatError("Testcase format error: {}".format(file_path)) + raise exceptions.FileFormatError("Testcase format error: {}".format(file_path)) key, test_block = item.popitem() if not isinstance(test_block, dict): - raise exception.FileFormatError("Testcase format error: {}".format(file_path)) + raise exceptions.FileFormatError("Testcase format error: {}".format(file_path)) if key == "config": testset["config"].update(test_block) @@ -261,7 +262,7 @@ class TestcaseLoader(object): def_args = block.get("function_meta").get("args", []) if len(call_args) != len(def_args): - raise exception.ParamsError("call args mismatch defined args!") + raise exceptions.ParamsError("call args mismatch defined args!") args_mapping = {} for index, item in enumerate(def_args): @@ -289,10 +290,10 @@ class TestcaseLoader(object): if not block: err_msg = "{} not found!".format(name) if ref_type == "api": - raise exception.ApiNotFound(err_msg) + raise exceptions.ApiNotFound(err_msg) else: # ref_type == "suite": - raise exception.SuiteNotFound(err_msg) + raise exceptions.SuiteNotFound(err_msg) return block @@ -380,7 +381,7 @@ class TestcaseLoader(object): testcases_list = [testset] else: testcases_list = [] - except exception.FileFormatError: + except exceptions.FileFormatError: testcases_list = [] else: @@ -407,7 +408,7 @@ def parse_validator(validator): } """ if not isinstance(validator, dict): - raise exception.ParamsError("invalid validator: {}".format(validator)) + raise exceptions.ParamsError("invalid validator: {}".format(validator)) if "check" in validator and len(validator) > 1: # format1 @@ -418,7 +419,7 @@ def parse_validator(validator): elif "expected" in validator: expect_value = validator.get("expected") else: - raise exception.ParamsError("invalid validator: {}".format(validator)) + raise exceptions.ParamsError("invalid validator: {}".format(validator)) comparator = validator.get("comparator", "eq") @@ -428,12 +429,12 @@ def parse_validator(validator): compare_values = validator[comparator] if not isinstance(compare_values, list) or len(compare_values) != 2: - raise exception.ParamsError("invalid validator: {}".format(validator)) + raise exceptions.ParamsError("invalid validator: {}".format(validator)) check_item, expect_value = compare_values else: - raise exception.ParamsError("invalid validator: {}".format(validator)) + raise exceptions.ParamsError("invalid validator: {}".format(validator)) return { "check": check_item, @@ -627,7 +628,9 @@ def substitute_variables_with_mapping(content, mapping): # content is a variable content = value else: - content = content.replace(var, str(value)) + if not isinstance(value, str): + value = builtin_str(value) + content = content.replace(var, value) return content @@ -711,7 +714,7 @@ def parse_parameters(parameters, testset_path=None): # e.g. [{'app_version': '2.8.5'}, {'app_version': '2.8.6'}] # e.g. [{"username": "user1", "password": "111111"}, {"username": "user2", "password": "222222"}] if not isinstance(parsed_parameter_content, list): - raise exception.ParamsError("parameters syntax error!") + raise exceptions.ParamsError("parameters syntax error!") parameter_content_list = [ # get subset by parameter name @@ -769,13 +772,13 @@ class TestcaseParser(object): if item_name in self.variables: return self.variables[item_name] else: - raise exception.ParamsError("bind item should only be function or variable.") + raise exceptions.ParamsError("bind item should only be function or variable.") try: assert self.file_path is not None return utils.search_conf_item(self.file_path, item_type, item_name) - except (AssertionError, exception.FunctionNotFound): - raise exception.ParamsError( + except (AssertionError, exceptions.FunctionNotFound): + raise exceptions.ParamsError( "{} is not defined in bind {}s!".format(item_name, item_type)) def get_bind_function(self, func_name): @@ -850,9 +853,12 @@ class TestcaseParser(object): content = variable_value else: # content contains one or several variables + if not isinstance(variable_value, str): + variable_value = builtin_str(variable_value) + content = content.replace( "${}".format(variable_name), - str(variable_value), 1 + variable_value, 1 ) return content diff --git a/httprunner/utils.py b/httprunner/utils.py index 3a508d86..edc98c73 100644 --- a/httprunner/utils.py +++ b/httprunner/utils.py @@ -16,8 +16,8 @@ import types from datetime import datetime import yaml -from httprunner import exception, logger -from httprunner.compat import OrderedDict, is_py2, is_py3 +from httprunner import exceptions, logger +from httprunner.compat import OrderedDict, basestring, is_py2, is_py3, str from requests.structures import CaseInsensitiveDict SECRET_KEY = "DebugTalk" @@ -54,13 +54,13 @@ class FileUtils(object): # testcase file content is empty err_msg = u"Testcase file content is empty: {}".format(file_path) logger.log_error(err_msg) - raise exception.FileFormatError(err_msg) + raise exceptions.FileFormatError(err_msg) elif not isinstance(content, (list, dict)): # testcase file content does not match testcase format err_msg = u"Testcase file content format invalid: {}".format(file_path) logger.log_error(err_msg) - raise exception.FileFormatError(err_msg) + raise exceptions.FileFormatError(err_msg) @staticmethod def _load_yaml_file(yaml_file): @@ -78,10 +78,10 @@ class FileUtils(object): with io.open(json_file, encoding='utf-8') as data_file: try: json_content = json.load(data_file) - except exception.JSONDecodeError: + except exceptions.JSONDecodeError: err_msg = u"JSONDecodeError: JSON file format error: {}".format(json_file) logger.log_error(err_msg) - raise exception.FileFormatError(err_msg) + raise exceptions.FileFormatError(err_msg) FileUtils._check_format(json_file, json_content) return json_content @@ -117,7 +117,7 @@ class FileUtils(object): @staticmethod def load_file(file_path): if not os.path.isfile(file_path): - raise exception.FileNotFoundError("{} does not exist.".format(file_path)) + raise exceptions.FileNotFound("{} does not exist.".format(file_path)) file_suffix = os.path.splitext(file_path)[1].lower() if file_suffix == '.json': @@ -172,7 +172,7 @@ class FileUtils(object): def query_json(json_content, query, delimiter='.'): """ Do an xpath-like query with json_content. - @param (json_content) json_content + @param (dict/list/string) json_content json_content = { "ids": [1, 2, 3, 4], "person": { @@ -186,23 +186,30 @@ def query_json(json_content, query, delimiter='.'): } @param (str) query "person.name.first_name" => "Leo" + "person.name.first_name.0" => "L" "person.cities.0" => "Guangzhou" @return queried result """ - if json_content == "": - raise exception.ResponseError("response content is empty!") - + raise_flag = False + response_body = u"response body: {}\n".format(json_content) try: for key in query.split(delimiter): - if isinstance(json_content, list): + if isinstance(json_content, (list, basestring)): json_content = json_content[int(key)] - elif isinstance(json_content, (dict, CaseInsensitiveDict)): + elif isinstance(json_content, dict): json_content = json_content[key] else: - raise exception.ParseResponseError( - "response content is in text format! failed to query key {}!".format(key)) + logger.log_error( + "invalid type value: {}({})".format(json_content, type(json_content))) + raise_flag = True except (KeyError, ValueError, IndexError): - raise exception.ParseResponseError("failed to query json when extracting response!") + raise_flag = True + + if raise_flag: + err_msg = u"Failed to extract! => {}\n".format(query) + err_msg += response_body + logger.log_error(err_msg) + raise exceptions.ExtractFailure(err_msg) return json_content @@ -333,9 +340,9 @@ def search_conf_item(start_path, item_type, item_name): # system root path err_msg = "{} not found in recursive upward path!".format(item_name) if item_type == "function": - raise exception.FunctionNotFound(err_msg) + raise exceptions.FunctionNotFound(err_msg) else: - raise exception.VariableNotFound(err_msg) + raise exceptions.VariableNotFound(err_msg) return search_conf_item(dir_path, item_type, item_name) @@ -422,7 +429,7 @@ def override_variables_binds(variables, new_mapping): elif isinstance(variables, (OrderedDict, dict)): variables_ordered_dict = variables else: - raise exception.ParamsError("variables error!") + raise exceptions.ParamsError("variables error!") return update_ordered_dict( variables_ordered_dict, @@ -507,7 +514,7 @@ def load_dot_env_file(path): return else: if not os.path.isfile(path): - raise exception.FileNotFoundError("env file not exist: {}".format(path)) + raise exceptions.FileNotFound("env file not exist: {}".format(path)) logger.log_info("Loading environment variables from {}".format(path)) with io.open(path, 'r', encoding='utf-8') as fp: diff --git a/tests/httpbin/hooks.yml b/tests/httpbin/hooks.yml index 87339639..c35c306c 100644 --- a/tests/httpbin/hooks.yml +++ b/tests/httpbin/hooks.yml @@ -32,5 +32,5 @@ - eq: ["status_code", 500] - eq: ["headers.content-type", "html/text"] - eq: [json.headers.Host, "127.0.0.1:8888"] - - eq: [content.headers.Host, "127.0.0.1:3458"] - - eq: [text.headers.Host, "127.0.0.1:3458"] + - eq: [content.headers.Host, "127.0.0.1:8888"] + - eq: [text.headers.Host, "127.0.0.1:8888"] diff --git a/tests/test_context.py b/tests/test_context.py index 746c79e3..1d4220f9 100644 --- a/tests/test_context.py +++ b/tests/test_context.py @@ -2,7 +2,7 @@ import os import time import requests -from httprunner import exception, response, runner, testcase +from httprunner import exceptions, response, runner, testcase from httprunner.context import Context from httprunner.utils import FileUtils, gen_md5 from tests.base import ApiServerUnittest @@ -270,9 +270,8 @@ class VariableBindsUnittest(ApiServerUnittest): ] self.context.bind_variables(variables) - with self.assertRaises(exception.ValidationError): - evaluated_validators = self.context.eval_validators(validators, resp_obj) - self.context.validate(evaluated_validators) + with self.assertRaises(exceptions.ValidationFailure): + self.context.validate(validators, resp_obj) validators = [ {"eq": ["$resp_status_code", 201]}, @@ -291,8 +290,7 @@ class VariableBindsUnittest(ApiServerUnittest): } self.context.bind_functions(functions) - evaluated_validators = self.context.eval_validators(validators, resp_obj) - self.context.validate(evaluated_validators) + self.context.validate(validators, resp_obj) def test_validate_exception(self): url = "http://127.0.0.1:5000/" @@ -307,9 +305,8 @@ class VariableBindsUnittest(ApiServerUnittest): variables = [] self.context.bind_variables(variables) - with self.assertRaises(exception.ParamsError): - evaluated_validators = self.context.eval_validators(validators, resp_obj) - self.context.validate(evaluated_validators) + with self.assertRaises(exceptions.ParamsError): + self.context.validate(validators, resp_obj) # expected value missed in variables mapping variables = [ @@ -317,6 +314,5 @@ class VariableBindsUnittest(ApiServerUnittest): ] self.context.bind_variables(variables) - with self.assertRaises(exception.ValidationError): - evaluated_validators = self.context.eval_validators(validators, resp_obj) - self.context.validate(evaluated_validators) + with self.assertRaises(exceptions.ValidationFailure): + self.context.validate(validators, resp_obj) diff --git a/tests/test_httprunner.py b/tests/test_httprunner.py index cadcae15..2b5ad47c 100644 --- a/tests/test_httprunner.py +++ b/tests/test_httprunner.py @@ -2,7 +2,7 @@ import os import shutil from httprunner import HttpRunner -from httprunner.exception import FileNotFoundError +from httprunner.exceptions import FileNotFound from tests.base import ApiServerUnittest @@ -163,5 +163,5 @@ class TestHttpRunner(ApiServerUnittest): self.assertEqual(os.environ["UserName"], "debugtalk") def test_load_env_path_not_exist(self): - with self.assertRaises(FileNotFoundError): + with self.assertRaises(FileNotFound): HttpRunner(dot_env_path="not_exist.env").run(self.testset_path) diff --git a/tests/test_response.py b/tests/test_response.py index f0879b3e..71d74583 100644 --- a/tests/test_response.py +++ b/tests/test_response.py @@ -1,6 +1,6 @@ import requests -from httprunner import exception, response, utils -from httprunner.compat import bytes +from httprunner import exceptions, response, utils +from httprunner.compat import bytes, str from tests.base import ApiServerUnittest @@ -27,7 +27,84 @@ class TestResponse(ApiServerUnittest): resp_obj = response.ResponseObject(resp) self.assertEqual(bytes, type(resp_obj.content)) - def test_extract_response_json(self): + def test_extract_response_status_code(self): + resp = requests.get(url="http://127.0.0.1:3458/status/200") + resp_obj = response.ResponseObject(resp) + + extract_binds_list = [ + {"resp_status_code": "status_code"} + ] + extract_binds_dict = resp_obj.extract_response(extract_binds_list) + + self.assertEqual( + extract_binds_dict["resp_status_code"], + 200 + ) + + extract_binds_list = [ + {"resp_status_code": "status_code.xx"} + ] + with self.assertRaises(exceptions.ParamsError): + resp_obj.extract_response(extract_binds_list) + + def test_extract_response_encoding_ok_reason_url(self): + resp = requests.get(url="http://127.0.0.1:3458/status/200") + resp_obj = response.ResponseObject(resp) + + extract_binds_list = [ + {"resp_encoding": "encoding"}, + {"resp_ok": "ok"}, + {"resp_reason": "reason"}, + {"resp_url": "url"} + ] + extract_binds_dict = resp_obj.extract_response(extract_binds_list) + + self.assertEqual(extract_binds_dict["resp_encoding"], "utf-8") + self.assertEqual(extract_binds_dict["resp_ok"], True) + self.assertEqual(extract_binds_dict["resp_reason"], "OK") + self.assertEqual(extract_binds_dict["resp_url"], "http://127.0.0.1:3458/status/200") + + extract_binds_list = [{"resp_encoding": "encoding.xx"}] + with self.assertRaises(exceptions.ParamsError): + resp_obj.extract_response(extract_binds_list) + + extract_binds_list = [{"resp_ok": "ok.xx"}] + with self.assertRaises(exceptions.ParamsError): + resp_obj.extract_response(extract_binds_list) + + extract_binds_list = [{"resp_reason": "reason.xx"}] + with self.assertRaises(exceptions.ParamsError): + resp_obj.extract_response(extract_binds_list) + + extract_binds_list = [{"resp_url": "url.xx"}] + with self.assertRaises(exceptions.ParamsError): + resp_obj.extract_response(extract_binds_list) + + def test_extract_response_cookies(self): + resp = requests.get( + url="http://127.0.0.1:3458/cookies", + headers={ + "accept": "application/json" + } + ) + resp_obj = response.ResponseObject(resp) + + extract_binds_list = [ + {"resp_cookies": "cookies"} + ] + extract_binds_dict = resp_obj.extract_response(extract_binds_list) + self.assertEqual( + extract_binds_dict["resp_cookies"], + {} + ) + + extract_binds_list = [ + {"resp_cookies": "cookies.xx"} + ] + with self.assertRaises(exceptions.ExtractFailure): + resp_obj.extract_response(extract_binds_list) + + def test_extract_response_elapsed(self): resp = requests.post( url="http://127.0.0.1:3458/anything", json={ @@ -42,7 +119,68 @@ class TestResponse(ApiServerUnittest): } } ) - # resp.text + resp_obj = response.ResponseObject(resp) + + extract_binds_list = [ + {"resp_elapsed": "elapsed"} + ] + with self.assertRaises(exceptions.ParamsError): + resp_obj.extract_response(extract_binds_list) + + extract_binds_list = [ + {"resp_elapsed_microseconds": "elapsed.microseconds"}, + {"resp_elapsed_seconds": "elapsed.seconds"}, + {"resp_elapsed_days": "elapsed.days"}, + {"resp_elapsed_total_seconds": "elapsed.total_seconds"} + ] + extract_binds_dict = resp_obj.extract_response(extract_binds_list) + self.assertGreater(extract_binds_dict["resp_elapsed_microseconds"], 1000) + self.assertEqual(extract_binds_dict["resp_elapsed_seconds"], 0) + self.assertEqual(extract_binds_dict["resp_elapsed_days"], 0) + self.assertGreater(extract_binds_dict["resp_elapsed_total_seconds"], 0) + + extract_binds_list = [ + {"resp_elapsed": "elapsed.years"} + ] + with self.assertRaises(exceptions.ParamsError): + resp_obj.extract_response(extract_binds_list) + + def test_extract_response_headers(self): + resp = requests.get(url="http://127.0.0.1:3458/status/200") + resp_obj = response.ResponseObject(resp) + + extract_binds_list = [ + {"resp_headers": "headers"}, + {"resp_headers_content_type": "headers.Content-Type"}, + {"resp_headers_content_type_lowercase": "headers.content-type"} + ] + extract_binds_dict = resp_obj.extract_response(extract_binds_list) + self.assertIn("Content-Type", extract_binds_dict["resp_headers"]) + self.assertIn("text/html", extract_binds_dict["resp_headers_content_type"]) + self.assertIn("text/html", extract_binds_dict["resp_headers_content_type_lowercase"]) + + extract_binds_list = [ + {"resp_headers_xxx": "headers.xxx"} + ] + with self.assertRaises(exceptions.ExtractFailure): + resp_obj.extract_response(extract_binds_list) + + def test_extract_response_body_json(self): + resp = requests.post( + url="http://127.0.0.1:3458/anything", + json={ + 'success': False, + "person": { + "name": { + "first_name": "Leo", + "last_name": "Lee", + }, + "age": 29, + "cities": ["Guangzhou", "Shenzhen"] + } + } + ) + # resp.json() # { # "args": {}, # "data": "{\"success\": false, \"person\": {\"name\": {\"first_name\": \"Leo\", \"last_name\": \"Lee\"}, \"age\": 29, \"cities\": [\"Guangzhou\", \"Shenzhen\"]}}", @@ -77,7 +215,6 @@ class TestResponse(ApiServerUnittest): # } extract_binds_list = [ - {"resp_status_code": "status_code"}, {"resp_headers_content_type": "headers.content-type"}, {"resp_content_body_success": "json.json.success"}, {"resp_content_content_success": "content.json.success"}, @@ -88,10 +225,6 @@ class TestResponse(ApiServerUnittest): resp_obj = response.ResponseObject(resp) extract_binds_dict = resp_obj.extract_response(extract_binds_list) - self.assertEqual( - extract_binds_dict["resp_status_code"], - 200 - ) self.assertEqual( extract_binds_dict["resp_headers_content_type"], "application/json" @@ -117,6 +250,35 @@ class TestResponse(ApiServerUnittest): "Shenzhen" ) + def test_extract_response_body_html(self): + resp = requests.get(url="http://127.0.0.1:3458/") + resp_obj = response.ResponseObject(resp) + + extract_binds_list = [ + {"resp_content": "content"} + ] + extract_binds_dict = resp_obj.extract_response(extract_binds_list) + + self.assertIsInstance(extract_binds_dict["resp_content"], str) + self.assertIn("python-requests.org", extract_binds_dict["resp_content"]) + + extract_binds_list = [ + {"resp_content": "content.xxx"} + ] + with self.assertRaises(exceptions.ExtractFailure): + resp_obj.extract_response(extract_binds_list) + + def test_extract_response_others(self): + resp = requests.get(url="http://127.0.0.1:3458/status/200") + resp_obj = response.ResponseObject(resp) + + extract_binds_list = [ + {"resp_others_encoding": "encoding"}, + {"resp_others_history": "history"} + ] + with self.assertRaises(exceptions.ParamsError): + resp_obj.extract_response(extract_binds_list) + def test_extract_response_fail(self): resp = requests.post( url="http://127.0.0.1:3458/anything", @@ -138,7 +300,7 @@ class TestResponse(ApiServerUnittest): ] resp_obj = response.ResponseObject(resp) - with self.assertRaises(exception.ParseResponseError): + with self.assertRaises(exceptions.ExtractFailure): resp_obj.extract_response(extract_binds_list) extract_binds_list = [ @@ -146,7 +308,7 @@ class TestResponse(ApiServerUnittest): ] resp_obj = response.ResponseObject(resp) - with self.assertRaises(exception.ParseResponseError): + with self.assertRaises(exceptions.ExtractFailure): resp_obj.extract_response(extract_binds_list) def test_extract_response_json_string(self): @@ -202,7 +364,7 @@ class TestResponse(ApiServerUnittest): {"resp_content_key1": "LB123.*RB789"} ] resp_obj = response.ResponseObject(resp) - with self.assertRaises(exception.ParamsError): + with self.assertRaises(exceptions.ParamsError): resp_obj.extract_response(extract_binds_list) def test_extract_response_empty(self): @@ -225,5 +387,5 @@ class TestResponse(ApiServerUnittest): {"resp_content_body": "content.data.def"} ] resp_obj = response.ResponseObject(resp) - with self.assertRaises(exception.ParseResponseError): + with self.assertRaises(exceptions.ExtractFailure): resp_obj.extract_response(extract_binds_list) diff --git a/tests/test_runner.py b/tests/test_runner.py index 390956eb..dee4b754 100644 --- a/tests/test_runner.py +++ b/tests/test_runner.py @@ -1,7 +1,7 @@ import os import time -from httprunner import HttpRunner, exception, runner +from httprunner import HttpRunner, exceptions, runner from httprunner.testcase import TestcaseLoader from httprunner.utils import FileUtils, deep_update_dict from tests.base import ApiServerUnittest @@ -66,7 +66,7 @@ class TestRunner(ApiServerUnittest): ] } - with self.assertRaises(exception.ValidationError): + with self.assertRaises(exceptions.ValidationFailure): self.test_runner.run_test(test) def test_run_testset_with_hooks(self): diff --git a/tests/test_testcase.py b/tests/test_testcase.py index 2e4af88c..b7ef5191 100644 --- a/tests/test_testcase.py +++ b/tests/test_testcase.py @@ -3,9 +3,7 @@ import time import unittest from httprunner import testcase -from httprunner.exception import (ApiNotFound, FileFormatError, - FileNotFoundError, ParamsError, - SuiteNotFound) +from httprunner.exceptions import ApiNotFound, ParamsError, SuiteNotFound from httprunner.testcase import TestcaseLoader diff --git a/tests/test_utils.py b/tests/test_utils.py index 76bd64d8..7b6e24dc 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -2,7 +2,7 @@ import os import shutil import unittest -from httprunner import exception, utils +from httprunner import exceptions, utils from httprunner.compat import OrderedDict from httprunner.utils import FileUtils from tests.base import ApiServerUnittest @@ -16,7 +16,7 @@ class TestFileUtils(unittest.TestCase): with open(yaml_tmp_file, 'w') as f: f.write("") - with self.assertRaises(exception.FileFormatError): + with self.assertRaises(exceptions.FileFormatError): FileUtils._load_yaml_file(yaml_tmp_file) os.remove(yaml_tmp_file) @@ -25,7 +25,7 @@ class TestFileUtils(unittest.TestCase): with open(yaml_tmp_file, 'w') as f: f.write("abc") - with self.assertRaises(exception.FileFormatError): + with self.assertRaises(exceptions.FileFormatError): FileUtils._load_yaml_file(yaml_tmp_file) os.remove(yaml_tmp_file) @@ -37,7 +37,7 @@ class TestFileUtils(unittest.TestCase): with open(json_tmp_file, 'w') as f: f.write("") - with self.assertRaises(exception.FileFormatError): + with self.assertRaises(exceptions.FileFormatError): FileUtils._load_json_file(json_tmp_file) os.remove(json_tmp_file) @@ -46,7 +46,7 @@ class TestFileUtils(unittest.TestCase): with open(json_tmp_file, 'w') as f: f.write("{}") - with self.assertRaises(exception.FileFormatError): + with self.assertRaises(exceptions.FileFormatError): FileUtils._load_json_file(json_tmp_file) os.remove(json_tmp_file) @@ -55,14 +55,14 @@ class TestFileUtils(unittest.TestCase): with open(json_tmp_file, 'w') as f: f.write("abc") - with self.assertRaises(exception.FileFormatError): + with self.assertRaises(exceptions.FileFormatError): FileUtils._load_json_file(json_tmp_file) os.remove(json_tmp_file) def test_load_testcases_bad_filepath(self): testcase_file_path = os.path.join(os.getcwd(), 'tests/data/demo') - with self.assertRaises(exception.FileNotFoundError): + with self.assertRaises(exceptions.FileNotFound): FileUtils.load_file(testcase_file_path) def test_load_json_testcases(self): @@ -163,11 +163,11 @@ class TestUtils(ApiServerUnittest): self.assertEqual(result, 3) query = "ids.str_key" - with self.assertRaises(exception.ParseResponseError): + with self.assertRaises(exceptions.ExtractFailure): utils.query_json(json_content, query) query = "ids.5" - with self.assertRaises(exception.ParseResponseError): + with self.assertRaises(exceptions.ExtractFailure): utils.query_json(json_content, query) query = "person.age" @@ -175,7 +175,7 @@ class TestUtils(ApiServerUnittest): self.assertEqual(result, 29) query = "person.not_exist_key" - with self.assertRaises(exception.ParseResponseError): + with self.assertRaises(exceptions.ExtractFailure): utils.query_json(json_content, query) query = "person.cities.0" @@ -186,16 +186,9 @@ class TestUtils(ApiServerUnittest): result = utils.query_json(json_content, query) self.assertEqual(result, "Leo") - def test_query_json_content_is_text(self): - json_content = "" - query = "key" - with self.assertRaises(exception.ResponseError): - utils.query_json(json_content, query) - - json_content = "content" - query = "key" - with self.assertRaises(exception.ParseResponseError): - utils.query_json(json_content, query) + query = "person.name.first_name.0" + result = utils.query_json(json_content, query) + self.assertEqual(result, "L") def test_get_uniform_comparator(self): self.assertEqual(utils.get_uniform_comparator("eq"), "equals") @@ -302,7 +295,7 @@ class TestUtils(ApiServerUnittest): self.assertIn("gen_md5", functions_dict) self.assertNotIn("urllib", functions_dict) - with self.assertRaises(exception.FileNotFoundError): + with self.assertRaises(exceptions.FileNotFoundError): utils.get_imported_module_from_file("tests/debugtalk2.py") def test_search_conf_function(self): @@ -314,10 +307,10 @@ class TestUtils(ApiServerUnittest): self.assertTrue(utils.is_function(("_", gen_md5))) self.assertEqual(gen_md5("abc"), "900150983cd24fb0d6963f7d28e17f72") - with self.assertRaises(exception.FunctionNotFound): + with self.assertRaises(exceptions.FunctionNotFound): utils.search_conf_item("tests/data/subfolder/test.yml", "function", "func_not_exist") - with self.assertRaises(exception.FunctionNotFound): + with self.assertRaises(exceptions.FunctionNotFound): utils.search_conf_item("/user/local/bin", "function", "gen_md5") def test_search_conf_variable(self): @@ -329,10 +322,10 @@ class TestUtils(ApiServerUnittest): self.assertTrue(utils.is_variable(("SECRET_KEY", SECRET_KEY))) self.assertEqual(SECRET_KEY, "DebugTalk") - with self.assertRaises(exception.VariableNotFound): + with self.assertRaises(exceptions.VariableNotFound): utils.search_conf_item("tests/data/subfolder/test.yml", "variable", "variable_not_exist") - with self.assertRaises(exception.VariableNotFound): + with self.assertRaises(exceptions.VariableNotFound): utils.search_conf_item("/user/local/bin", "variable", "SECRET_KEY") def test_is_variable(self): @@ -443,7 +436,7 @@ class TestUtils(ApiServerUnittest): map_list = "invalid" override_mapping = {"a": 3, "c": 4} - with self.assertRaises(exception.ParamsError): + with self.assertRaises(exceptions.ParamsError): utils.override_variables_binds(map_list, override_mapping) def test_create_scaffold(self):