Ejemplo n.º 1
0
def display(*args, format_print=True, mode=None, log_level='info'):
    """
    + 说明:
        打印输出到console和日志

    + 用法:
        输入:display('dcn','test',format_print=True)

        输出:False

        >>> ################################################################################
        >>> # dcn                                                                          #
        >>> # test                                                                         #
        >>> ################################################################################

        输入:display('dcn','test',format_print=False)

        输出:dcn test

    :param mode: display模式默认为log输出,如果为print方式,指定mode='print'
    :param args: 显示到console/log文件的message
    :param format_print: 是否统一格式化输出,默认为True
    :param log_level: 日志级别
    """
    output = (print_format(*args),) if format_print else (str(o) for o in args)
    if mode == 'print':
        print(*output)
    else:
        from .log import log
        log(*output, level=log_level)
Ejemplo n.º 2
0
def color(exclude=None):
    """
    + 说明:
        在串口返回随机颜色字符串,随机范围如下(可以通过exclude缩小随机范围)

        {

        'BLACK', 'BLUE', 'CYAN', 'GREEN', 'LIGHTBLACK_EX',
        'LIGHTBLUE_EX', 'LIGHTCYAN_EX', 'LIGHTGREEN_EX',
        'LIGHTMAGENTA_EX', 'LIGHTRED_EX', 'LIGHTWHITE_EX',
        'LIGHTYELLOW_EX', 'MAGENTA', 'RED', 'RESET', 'WHITE', 'YELLOW'

        }

    :param exclude: 随机颜色中排除指定颜色
    :return: 颜色字符串
    """
    try:
        import random
        colors = ['BLACK', 'BLUE', 'CYAN', 'GREEN', 'LIGHTBLACK_EX',
                  'LIGHTBLUE_EX', 'LIGHTCYAN_EX', 'LIGHTGREEN_EX',
                  'LIGHTMAGENTA_EX', 'LIGHTRED_EX', 'LIGHTWHITE_EX',
                  'LIGHTYELLOW_EX', 'MAGENTA', 'RED', 'RESET', 'WHITE', 'YELLOW']
        if exclude:
            assert isinstance(exclude, (str,)), '参数非String类型'
            if isinstance(exclude, (str,)) and exclude.upper() in colors:
                colors.remove(exclude.upper())
        return random.choice(colors)
    except Exception as e:
        from .log import log
        log("{} error: {}".format(color.__name__, str(e)))
Ejemplo n.º 3
0
    def __new__(mcs, name, bases, attr):
        funcs, cases = Tool.filter_test_case(attr)
        for raw_case_name, raw_case in cases:
            if not hasattr(raw_case, CASE_TAG_FLAG):
                setattr(raw_case, CASE_TAG_FLAG,
                        {const.Tag.SMOKE, const.Tag.FULL
                         })  # 没有指定tag的用例,默认有SMOKE和FULL标记

            # 注入用例信息
            case_info = "{}.{}".format(raw_case.__module__, raw_case.__name__)
            setattr(raw_case, CASE_INFO_FLAG, case_info)

            # 检查用例描述
            if const.CHECK_CASE_DOC and not raw_case.__doc__:
                log("{}没有用例描述".format(case_info), level='warning')

            # 过滤不执行的用例
            if not getattr(raw_case, CASE_TAG_FLAG) & set(const.RUN_CASE):
                continue

            # 注入测试数据
            if hasattr(raw_case, CASE_DATA_FLAG):
                funcs.update(
                    Tool.create_case_with_case_data(raw_case_name, raw_case))
            else:
                funcs.update(
                    Tool.create_case_without_case_data(raw_case_name,
                                                       raw_case))

        return super(Meta, mcs).__new__(mcs, name, bases, funcs)
Ejemplo n.º 4
0
def setup_project(start_path=os.getcwd()):
    """
    从指定起始路径扫描指定后缀配置文件
    :param start_path: 默认值为当前根路径
    :return: None
    """
    def parse_conf(conf_file_path):
        log(f'开始从{conf_file_path}中解析配置文件')
        _config = load_file(conf_file_path)
        for key, value in _config.items():
            if conf_file_path.suffix == '.ini':
                for sub_key, sub_value in value.items():
                    settings.set(sub_key, sub_value)
            else:
                settings.set(key, value)  # 解析json??或者其他配置文件

    log(f'开始从{start_path}扫描...', level='debug')
    # 扫描当前项目文件夹,根据项目需求只扫描外面2层目录即可,正则过滤无效的.或者__打头的隐藏或者无效目录
    target_dir = scan_depth_dir(start_path, depth=2, condition='[A-Za-z]+')
    for dir_path in target_dir:  # 魔改目录名称
        settings.set(f"DCN_{dir_path.stem.upper()}_PATH", dir_path)
    # 扫描当前项目config中的ini配置文件(目前只扫描ini/json文件,后续可以进行扩展)
    target_conf_path = scan_file(settings.get('DCN_CONFIG_PATH'),
                                 condition=('[\w]+\.ini', '[\w]+\.json'))
    for conf in target_conf_path:
        parse_conf(conf)
    # 扫描当前项目testdata中的xls和xlsx的文件,正则排除以~$开头的临时文件
    target_file = scan_file(settings.get('DCN_TESTDATA_PATH'),
                            condition=('[\w]+\.xls', '[\w]+\.xlsx'))
    for _file in target_file:
        settings.set(_file.stem, _file)

    def modify_log_report_config():
        """
        根据项目实际需要修改log和report目录,存放结构如下
        test_report/current_time/console.log
        test_report/current_time/report.html
        :return: None
        """
        import time
        current_time = time.strftime("%Y%m%d%H%M%S")
        if settings.get('report_file_modify'):  # 根据配置文件中的策略修改日志和报告路径
            report_path = settings.get('DCN_TESTREPORT_PATH')
            settings.set('report_dir_path',
                         (report_path / current_time).__fspath__())
            settings.set('report_file',
                         (report_path / current_time /
                          settings.get('report_name')).__fspath__())
            if settings.log_file_modify:
                settings.set('log_dir_path',
                             (report_path / current_time).__fspath__())
                settings.set('log_file',
                             (report_path / current_time /
                              settings.get('log_name')).__fspath__())
        del time

    modify_log_report_config()  # 动态修改日志和报告存放路径和信息
    import sys
    sys.modules['library.conf'].settings = settings
Ejemplo n.º 5
0
 def wrap(*args, **kwargs):
     time.sleep(const.EXECUTE_INTERVAL)
     msg = "start to test {} ({}/{})".format(getattr(func, CASE_INFO_FLAG),
                                             getattr(func, CASE_ID_FLAG),
                                             Tool.total_case_num)
     log(msg, level='info')
     result = func(*args, **kwargs)
     return result
Ejemplo n.º 6
0
 def load(self, discovery):
     if discovery:
         self.parse(discovery)
     else:  # 默认加载方式 先从全局settings中获取,失败再从当前python文件中获取
         if settings.get('testcase_ini'):  # 尝试全局扫描到的配置文件
             log("使用自动扫描到的配置文件加载测试用例", level='info')
         else:  # 尝试通过py加载
             log("使用当前环境中的py变量加载测试用例", level='info')
Ejemplo n.º 7
0
 def parse_conf(conf_file_path):
     log(f'开始从{conf_file_path}中解析配置文件')
     _config = load_file(conf_file_path)
     for key, value in _config.items():
         if conf_file_path.suffix == '.ini':
             for sub_key, sub_value in value.items():
                 settings.set(sub_key, sub_value)
         else:
             settings.set(key, value)  # 解析json??或者其他配置文件
Ejemplo n.º 8
0
def setup():
    log('项目初始化环境设置中...')
    # 动态扫描整个项目目录,读取配置文件中的ini文件
    setup_project()
    # 动态读取配置文件中日志配置

    setup_log()

    # 动态读取配置文件中unittest配置
    setup_unittest()

    log('项目初始化完成...', color='yellow')
Ejemplo n.º 9
0
def setup_log():
    log_instance.logger = log_instance.setup(
        name=settings.logger_name,
        log_file=settings.log_file
        if settings.file_log_on else None,  # 用于判断是否生成日志文件
        console_level=settings.log_level_in_console,
        fmt=settings.log_fmt,
        max_bytes=settings.max_bytes_each,
        backup_count=settings.backup_count,
        logfile_level=settings.log_level_in_logfile,
        display_to_console=settings.console_log_on,
    )
    log('根据项目ini文件重新设置日志默认格式')
Ejemplo n.º 10
0
def check_dict_conflict(source):
    """
    检查判断字典value是否存在冲突

    :param source:
    :return:
    """
    _check = {}
    for _k, _v in source.items():
        if _v in _check:
            err_msg = f'{_v}冲突 {_check[_v]}--->{_k}存在冲突键值对,请注意检查'
            log('存在冲突键值对,请注意检查', level='error')
            raise Exception(err_msg)
        _check[_v] = _k
Ejemplo n.º 11
0
def assert_dir(dir_path):
    """
    + 说明:
        探测dir_path是否为存在的有目录路径,如果不是返回InvalidPathDir异常。

    :param dir_path: 文件目录路径名称(string)/WindowPath实例。
    :return: WindowsPath实例
    """
    dir_path = as_path(dir_path)
    if not dir_path.is_dir():
        err_msg = f'{dir_path} 不是有效目录'
        log(err_msg, level='error')
        raise InvalidPathDir(err_msg)
    return dir_path
Ejemplo n.º 12
0
def assert_file(file_path):
    """
    + 说明:
        测file_path是否为存在的有效文件路径,如果不是返回FileNotFound异常。

    :param file_path: 文件路径名称(string)/WindowPath实例。
    :return: WindowsPath instance
    """
    file_path = as_path(file_path)
    if not file_path.is_file():
        err_msg = f'{file_path} 不是有效文件目录'
        log(err_msg, level='error')
        raise FileNotFound(err_msg)
    return file_path
Ejemplo n.º 13
0
    def set(self, key, value):
        """优先选用的设置值的方式

        :param key: The key to store
        :param value: The value to store
        """
        value = parse_conf_data(value)

        key = key.strip()
        if key in self.store and value != self.store[key]:
            from library.log import log
            log(f'注意 {key} {self(key)} 修改成 {key} {value}', level='warning')
        setattr(self, key, value)
        self.store[key] = value
        self._deleted.discard(key)
Ejemplo n.º 14
0
def print_check_case(sheet_name, result):
    """
    + 说明:
        检查测试例step对错,打印并记录

    :param sheet_name: 测试用例sheet名称
    :param result: 测试结果列表
    """
    if 0 in result:  # 如果result列表中存在0表示测试用例失败
        log(f'{sheet_name} is FAILED!', result, level='error')
        r = False
    else:
        log(sheet_name + ' ' + 'is PASSED', result, level='info')
        r = True
    return r
Ejemplo n.º 15
0
 def parse(self, discovery):
     if isinstance(discovery, str):
         log("===解析字符串===")
         self._parse_str(discovery)
     elif isinstance(discovery, os.PathLike):
         log("===解析PathLike路径===")
         self._parse_path(discovery)
     elif ismodule(discovery):
         log("===解析模块===")
         self._suite.addTests(
             self.loadTestsFromModule(discovery, pattern=None))
     elif isclass(discovery):
         log("===解析类===")
         self._suite.addTests(self.loadTestsFromTestCase(discovery))
     elif isinstance(discovery, (list, tuple)):
         log("===解析Sequence===")
         for _discovery in discovery:
             self.parse(_discovery)
Ejemplo n.º 16
0
 def _send_request_safe_mode(self, method, url, **kwargs):
     """
     Send a HTTP request, and catch any exception that might occur due to connection problems.
     Safe mode has been removed from requests 1.x.
     """
     try:
         msg = "processed request:\n"
         msg += "> {method} {url}\n".format(method=method, url=url)
         msg += "> kwargs: {kwargs}".format(kwargs=kwargs)
         log(msg)
         return requests.Session.request(self, method, url, **kwargs)
     except (MissingSchema, InvalidSchema, InvalidURL):
         raise
     except RequestException as ex:
         resp = ApiResponse()
         resp.error = ex
         resp.status_code = 0  # with this status_code, content returns None
         resp.request = Request(method, url).prepare()
         return resp
Ejemplo n.º 17
0
 def display(self, response_json_or_text, method='POST'):
     """
     测试用例步骤输入输出显示
     :param method: http request method
     :param response_json_or_text: response json or text data
     :return:
     """
     msg = "\n[输入]:\n"
     msg += "> {method} {url}\n".format(method=method, url=self.url)
     if isinstance(response_json_or_text, dict):
         msg += "> kwargs: {kwargs}".format(
             kwargs=response_json_or_text.get('file') or self.data)
         dcn_raw_code = dcn_codes.get(response_json_or_text['status'])
         display_response = deepcopy(response_json_or_text)
         if dcn_raw_code:
             display_response['status'] = (display_response['status'],
                                           dcn_raw_code[0])
     else:
         display_response = response_json_or_text
     from pprint import pformat
     log(msg, level='info')
     log(f'\n[输出]:\n> response: {pformat(display_response)}', level='info')
Ejemplo n.º 18
0
def print_timer_context(test_case_name, *args, **kwargs):
    """
    + 说明:
        测试例开始/结束计时

        >>> with print_timer_context('hello, world') as doctest:
        >>>    print(doctest)

    :param test_case_name: 测试用例名称
    :param args: msg
    :param kwargs: other args
    :return: 格式化输出

    """
    start_time = datetime.datetime.now()
    try:
        log(r'{title} {switch} {time}'.format(title=test_case_name, switch='start at', time=str(start_time)),
            format_print=True, level='info', *args, **kwargs)
        yield 'for doctest use'
    finally:
        stop_time = datetime.datetime.now()
        log(r'{title} {switch} {time}'.format(title=test_case_name, switch='end at', time=str(stop_time)),
            'TestCase Duration Time:{time}'.format(time=duration(start_time, stop_time)),
            format_print=True, level='info', *args, **kwargs)
Ejemplo n.º 19
0
def ensure_dir(dir_path, path_type='parent'):
    """
    + 说明:
        确保指定路径文件的目录存在(不存在创建)。

    :param dir_path: 需要确保的路径参数。
    :param path_type: current或者parent。
    """
    path_mapping = {
        'current': as_path(dir_path),
        'parent': as_path(dir_path).parent
    }

    try:
        dir_abs = path_mapping[path_type]

        if not dir_abs.exists():
            msg = f'\n目标 -> {dir_path}\n创建 -> {dir_abs}'
            log(msg, level='info')
            dir_abs.mkdir(parents=True, exist_ok=True)
    except KeyError:
        err_msg = f"{path_type} 不是有效参数,有效参数为'parent' or 'current'"
        log(err_msg, level='error')
        raise InvalidOption(err_msg)
Ejemplo n.º 20
0
def as_path(path_name):
    """
    + 说明:
        1. string类型或者Path实例路径进行格式化和统一化,如果是string转换成Path实例,
        2. Path实例解析成绝对路径,
        3. string中存在 windows下文件名称非法字符为抛出异常,记录日志。

    >>> as_path('E:/1/2/3')
    WindowsPath('E:/1/2/3')
    >>> as_path('E:/1')
    WindowsPath('E:/1')

    :param path_name: 文件路径名称(string)/WindowPath实例。
    :return: WindowsPath实例。

    """
    try:
        if isinstance(path_name, Path):
            return path_name.resolve()
        return Path(path_name).resolve()
    except OSError as e:
        err_msg = f'路径名称{path_name}含有如下非法字符(\/:*?"<>|)'
        log(err_msg, level='error')
        raise InvalidPath(err_msg) from e
Ejemplo n.º 21
0
    def _parse_str(self, discovery):
        if discovery in sys.modules:  # 尝试本地命名空间中是否存在该模块存在
            log(f'\n===sys.modules===\n通过sys.modules导入{discovery}')
            self._suite.addTests(
                self.loadTestsFromModule(sys.modules[discovery], pattern=None))
        elif DOT_MODULE_MATH.match(discovery):  # 校验字符串合法性,匹配绝对路径
            if any([
                    not _.isidentifier() and _ not in kwlist
                    for _ in discovery.split('.')
            ]):
                raise InvalidArgument(f'非法参数{discovery}')
            else:
                name = discovery
                try:
                    self._suite.addTests(
                        self.loadTestsFromName(name))  # 直接进行解析
                    log(f'\n===绝对路径===\n通过loadTestsFromNames导入{discovery}')
                except (ModuleNotFoundError, Exception):  # no qa可能路径带有具体方法???
                    parts = discovery.split('.')
                    from importlib import import_module
                    while parts:
                        try:
                            mod = import_module('.'.join(parts))
                            if callable(getattr(mod, _class)):
                                self._suite.addTest(
                                    getattr(mod, _class)(_method))
                                log(f'\n===实例化对象引入===\n通过addTest导入{mod}{_class}{_method}'
                                    )
                                break
                        except ImportError:
                            _class = parts[-2]
                            _method = parts[-1]
                            parts = parts[:-2]
        elif discovery.startswith('.'):  # 如果为相对引入路径,结合top_level_dir进行相对引入
            name = valid_import_path(self.top_level_dir) + discovery
            self._suite.addTests(self.loadTestsFromName(name))
            log(f'\n===相对路径===\n通过loadTestsFromName导入{name}')

        else:
            try:
                if Path(discovery).resolve().is_dir() or Path(
                        discovery).resolve().is_file():  # 如果传入路径进行路径解析
                    self._parse_path(discovery)  # 委托给parse_path进行解析
                else:
                    raise InvalidPath(f'无效路径 {discovery}')  # 必须为有效路径
            except OSError:
                if '*' in discovery or '?' in discovery or '.' in discovery:  # 尝试解析例如E://testcase/test_*.py
                    parent = Path(discovery).parent  # 父路径
                    pattern = Path(discovery).parts[-1]  # 正则
                    if parent.is_dir():
                        glob_path_list = list(parent.glob(pattern))
                        if len(glob_path_list):
                            from pprint import pformat
                            log(f'\n===加载如下测试用例===\n{pformat(glob_path_list)}')
                            for _ in glob_path_list:
                                valid_import_path(_.parent)
                                self._suite.addTests(
                                    self.loadTestsFromName('.'.join(
                                        [valid_import_path(_.parent),
                                         _.stem])))
                        else:
                            raise TestCaseNotFound('没有找到测试用例')

                    else:
                        raise InvalidPath(f'无效路径 {discovery}')  # 必须为有效路径
                else:
                    raise InvalidArgument(f'无法解析参数 {discovery}')
Ejemplo n.º 22
0
 def _parse_path(self, discovery):
     log(" parse path", level='info')
     discovery = Path(discovery).resolve()
     if discovery.is_dir():  # 如果传入dir要求必须是测试用例路,不能是配置文件
         self._suite.addTests(
             self.discover(discovery, top_level_dir=self.top_level_dir))
         log("文件夹", level='info')
     elif discovery.is_file():  # 如果传入file要求必须是配置文件
         # todo
         if '.ini' == discovery.suffix:
             log("parse ini", level='info')
         elif '.json' == discovery.suffix:
             log('parse json', level='info')
         elif '.ymal' == discovery.suffix:
             log('parse toml', level='info')
         elif '.toml' == discovery.suffix:
             log('parse toml', level='info')
         elif '.py' == discovery.suffix:
             log('parse py', level='info')
         else:
             log('error')
     else:
         raise ParsePathError(f'无法解析{discovery}路径')
Ejemplo n.º 23
0
    def request(self, method, url, name=None, **kwargs):
        """
        Constructs and sends a :py:class:`requests.Request`.
        Returns :py:class:`requests.Response` object.

        :param method:
            method for the new :class:`Request` object.
        :param url:
            URL for the new :class:`Request` object.
        :param name: (optional)
            Placeholder, make compatible with Locust's HttpSession
        :param params: (optional)
            Dictionary or bytes to be sent in the query string for the :class:`Request`.
        :param data: (optional)
            Dictionary or bytes to send in the body of the :class:`Request`.
        :param headers: (optional)
            Dictionary of HTTP Headers to send with the :class:`Request`.
        :param cookies: (optional)
            Dict or CookieJar object to send with the :class:`Request`.
        :param files: (optional)
            Dictionary of ``'filename': file-like-objects`` for multipart encoding upload.
        :param auth: (optional)
            Auth tuple or callable to enable Basic/Digest/Custom HTTP Auth.
        :param timeout: (optional)
            How long to wait for the server to send data before giving up, as a float, or \
            a (`connect timeout, read timeout <user/advanced.html#timeouts>`_) tuple.
            :type timeout: float or tuple
        :param allow_redirects: (optional)
            Set to True by default.
        :type allow_redirects: bool
        :param proxies: (optional)
            Dictionary mapping protocol to the URL of the proxy.
        :param stream: (optional)
            whether to immediately download the response content. Defaults to ``False``.
        :param verify: (optional)
            if ``True``, the SSL cert will be verified. A CA_BUNDLE path can also be provided.
        :param cert: (optional)
            if String, path to ssl client cert file (.pem). If Tuple, ('cert', 'key') pair.
        """
        # record test name
        self.meta_data["name"] = name

        # record original request info
        self.meta_data["data"][0]["request"]["method"] = method
        self.meta_data["data"][0]["request"]["url"] = url
        kwargs.setdefault("timeout", 120)
        self.meta_data["data"][0]["request"].update(kwargs)

        # prepend url with hostname unless it's already an absolute URL
        url = build_url(self.base_url, url)

        start_timestamp = time.time()
        response = self._send_request_safe_mode(method, url, **kwargs)
        response_time_ms = round((time.time() - start_timestamp) * 1000, 2)

        # get the length of the content, but if the argument stream is set to True, we take
        # the size from the content-length header, in order to not trigger fetching of the body
        if kwargs.get("stream", False):
            content_size = int(dict(response.headers).get("content-length") or 0)
        else:
            content_size = len(response.content or "")

        # record the consumed time
        self.meta_data["stat"] = {
            "response_time_ms": response_time_ms,
            "elapsed_ms": response.elapsed.microseconds / 1000.0,
            "content_size": content_size
        }

        # record request and response histories, include 30X redirection
        response_list = response.history + [response]
        self.meta_data["data"] = [
            self.get_req_resp_record(resp_obj)
            for resp_obj in response_list
        ]

        try:
            response.raise_for_status()
        except RequestException as e:
            log(u"{exception}".format(exception=str(e)), level='error')
        else:
            log(
                """status_code: {}, response_time(ms): {} ms, response_length: {} bytes\n""".format(
                    response.status_code,
                    response_time_ms,
                    content_size
                ),
            )

        return response
Ejemplo n.º 24
0
    def api(self):
        """
        + 说明:
        输入需要测试的excel名称和excel中sheet名称,获取sheet表中每行的测试用例编号,测试用例名称,预制条件,url,
        data,request方法,响应码和错误检测码,测试每张sheet表中不同的测试例。


        + 测试结果判断:
              根据获取的响应码code[i]或者错误检测码error_code[i]来判断测试例的测试结果是否pass。

           1.当获取的code[i]不为空且获取返回结果的"status"和code[i]相等,这个时候是通过code[i]即响应码来判断测试结果的。

           2.当获取的code[i]为空且error_code[i]不为空且获取返回结果的”status“不为0,这个时候是通过error_code[i]即错误响应码来判断
           测试结果的,这时根据返回结果又分为三种情况:
             情况1:
                  返回的字典结果中字典的key等于"errors" or "addErrors" or "delErrors" or "updateErrors"且这些key对应的值为一个
               列表,如{'status': 7, 'errors': [{'id': 14, 'status': 1}, {'id': 7, 'status': 163}]},取key对应列表中的"status"
               或者"code"和error_code[i]做对比得出测试结果是否pass;
             情况2:
                  返回字典的key等于“result”且这个key对应的结果是一个字典,如{"status":4,"result":{"count":4,"errors":
               [{"index":5,"code":251},{"index":6,"code":252},{"index":7,"code":252},{"index":8,"code":252}]}},
               这时是通过key“count"或者key“result”对应字典的key“errors”对应的值key"index"或者“code”和error_code[i]做对比得出测试结果是否pass;
             情况3:
                  返回字典的key等于“result”且这个key对应的结果不是一个字典,这是一个容错判断,直接判断测试结果为fail;

           3.当获取的code[i]为空且获取返回结果的"status"等于0,这是个容错判断,直接判断由于功能问题导致的测试结果为fail;

           4.除上述外的其他情况,判断测试结果为fail。

        :return: 返回每张sheet中各个测试例结果的列表

        """
        # noinspection PyTypeChecker
        arguments = zip(self.seqs, self.names, self.urls, self.data,
                        self.methods, self.codes, self.error_codes)
        res = []
        for seq, name, url, data, method, code, error_code in arguments:
            full_url = settings.base_url + url
            with print_timer_context(f'{seq} {name}'):
                api = SessionMethod(full_url, data, seq)
                stat = {'status': None}
                request = {
                    "post": api.post,
                    "get": api.get,
                    "put": api.put,
                    "delete": api.delete,
                    "head": api.head,
                    "patch": api.patch,
                    "option": api.option,
                    "postuser": api.post_user,
                    "getuser": api.get_user,
                    "putuser": api.put_user,
                    "deleteuser": api.delete_user,
                    "postadmin": api.post_admin,
                    "getadmin": api.get_admin,
                    "putadmin": api.put_admin,
                    "deleteadmin": api.delete_admin,
                    "postnologin": api.post_no_login,
                    "getnologin": api.get_no_login,
                    "putnologin": api.put_no_login,
                    "deletenologin": api.delete_no_login
                }
                if method not in request:
                    log('interface test method is error', level='info')
                else:
                    stat = request[method]()
                # 下面代码作用为根据返回结果判断测试是否通过
                if code != "" and stat["status"] == code:
                    # 使用状态码code[]来判断的情况
                    log(
                        f'\n[结果]:\n'
                        f'> [Except Code: {int(code)} ] | [Actual Code: {stat["status"]}] TestCase {name}({seq}) is '
                        f'Passed',
                        level='info')
                    # log(f'[Match Code: {code} is OK!] TestCase {name} {seq} is Passed', level='info')
                    res.append(1)
                elif code == "" and error_code != "" and stat["status"] != 0:
                    # 使用状态码error_code[]来判断的情况,有一种特殊情况,预期不能创建成功的接口,在测试中创建成功会返回代码0,例如:
                    # {'status': 0, 'addErrors': None, 'updateErrors': None, 'delErrors': None}
                    for key in stat:
                        if (key == "errors" or key == "addErrors" or key == "delErrors" or key == "updateErrors") \
                                and isinstance(stat[key], list):
                            # 用于user->“组织机构用户组批量操作”or“批量删除用户组”测试判断
                            # {'status': 7, 'errors': [{'id': 14, 'status': 1}, {'id': 7, 'status': 163}]}
                            for d in range(len(stat[key])):
                                # 遍历关键字为"errors"or"addErrors"or"delErrors"or"updateErrors"的字典所对应值的列表,这里列表所对应
                                # 的元素个数是变化的
                                if stat[key][d].get("status") == error_code \
                                        or stat[key][d].get("code") == error_code:
                                    # 列表中每一个元素都是字典类型,取字典中关键字"status"对应的错误码,用来做判断
                                    log(f'[Match ErrorCode: {error_code} is OK!] TestCase {seq} is Passed',
                                        level='info')
                                    res.append(1)
                                    # 如果错误码和预期一致,目前遇到的返回码只涉及到关键字"errors"or"addErrors"or"delErrors"
                                    # or"updateErrors"中的一种,所以一旦找到就退出循环
                                    break
                            else:
                                log(f'[Not Match ErrorCode: {error_code}] TestCase {seq} is Failed',
                                    level='error')
                                res.append(0)
                                # 整个错误码对应列表中的字典元素都和预期错误码不一致,记为测试fail
                        elif (key == 'result') and isinstance(stat[key], dict):
                            # 用于user->"用户账号批量导入"测试判断
                            # {"status":4,"result":{"count":4,"errors":[{"index":5,"code":251},{"index":6,"code":252},
                            # {"index":7,"code":252},{"index":8,"code":252}]}}
                            if stat[key]['count'] == error_code or \
                                    ('errors' in stat[key] and error_code == 'errors'):
                                # 判断count或者errors是否与错误检查点一致
                                log(f'[Match ErrorCode: {error_code} is OK!] TestCase {seq} is Passed',
                                    level='info')
                                res.append(1)
                            else:
                                for b in range(len(stat[key]['errors'])):
                                    # 遍历关键字为"errors"的字典所对应值的列表,这里列表所对应的元素个数是变化的
                                    if stat[key]['errors'][b].get("index") == error_code \
                                            or stat[key]['errors'][b].get("code") == error_code:
                                        # 列表中每一个元素都是字典类型,取字典中关键字"index"或者“code”对应的错误码,用来做判断
                                        log(f'[Match ErrorCode: {error_code} is OK!] TestCase {seq} is Passed',
                                            level='info')
                                        res.append(1)
                                        # 如果错误码和预期一致,目前遇到的返回码只涉及到关键字"code"or"index"中的一种,
                                        # 所以一旦找到就退出循环
                                        break
                                else:
                                    log(f'[Not Match ErrorCode: {error_code}] TestCase {seq} is Failed',
                                        level='error')
                                    res.append(0)
                                    # 整个错误码对应列表中的字典元素都和预期错误码不一致,记为测试fail
                        elif (key
                              == 'result') and not isinstance(stat[key], dict):
                            # org->组织机构导入-IP格式不正确{"status":3,"result":null}
                            log(f'[Not Match ErrorCode: {error_code}] TestCase {seq} is Failed',
                                level='error')
                            res.append(0)

                elif code == "" and stat["status"] == 0:
                    log(f'[The Reason for TestCase {seq} Failure is function problem',
                        level='error')
                    res.append(0)
                else:
                    log(
                        f'\n[结果]:\n'
                        f"> [Except Code: {str(code).strip('.0')} ] | [Actual Code: {stat['status']}] "
                        f"TestCase {name}({seq}) is Failed",
                        level='error')
                    res.append(0)
        return res
Ejemplo n.º 25
0
 def wrap(func):
     if hasattr(func, CASE_DATA_FLAG):
         log("{}的测试数据只能初始化一次".format(func.__name__), level='error')
     setattr(func, CASE_DATA_FLAG, values)
     setattr(func, CASE_DATA_UNPACK_FLAG, unpack)
     return func
Ejemplo n.º 26
0
 def _log(self):
     log(Template(LOG_TEMPLATE).render(items=enumerate(self.log_content)))
Ejemplo n.º 27
0
 def log_print(req_resp_dict, r_type):
     msg = "\n================== {} details ==================\n".format(r_type)
     for key, value in req_resp_dict[r_type].items():
         msg += "{:<16} : {}\n".format(key, repr(value))
     log(msg)