def request(data, context): log.info('执行请求步骤: ', data.get(NAME)) req = data.get(REQUEST) variables = context.get(VAIABLES) # todo 改为result config = context.get(CONFIG) context.setdefault(REQUEST_SESSION, requests.session()) session = context.get(REQUEST_SESSION) if config and isinstance(config, dict): # 组装url pack_url(config, req) # 设置默认请求想 set_default_request(config, session) # 设置默认方法 set_default_method(req) if req and isinstance(req, dict): res = session.request(**req) # 注册变量 variables['response'] = res variables['response_json'] = res.json() # todo variables['content'] = res.json() # todo variables['status_code'] = res.status_code log.debug('请求数据:', req, '响应数据:', res.text) return res
def _run_step_with_times(self, step, times: int, context): """多轮运行""" ensure_type(times, int) results = [] for i in range(times): logging.info(f' 运行Step {step} 第{i + 1}轮') results.append(self._run_step(step, context)) return results
def sequence_run(self): results = [] for i in range(self.times): log.info('执行步骤:', self.name, f'第{i+1}轮' if self.times > 1 else '') result = self.run(self.data, self.context) results.append(result) self.context['result'] = self.result return results
def do_extract(self, extract: list): """处理提取变量""" for line in extract: logging.info(f' 提取变量: {line}') key, expr = tuple(line.items())[0] ensure_type(expr, str) ensure_type(key, str) value = self._context.dot_get(expr) logging.info(f' 注册变量: {key}={value}') self._context.register_variables({key: value})
def should_skip(self): skip = self.skip if isinstance(skip, str): try: skip = eval(skip) except Exception as ex: log.exception(ex) log.info('跳过步骤:', self.name, '原因: skip表达式出错') return True if skip: log.info('跳过步骤:', self.name, f'原因: skip={self.skip}') return skip
def parallel_run(self): times = self.times // self.concurrency results = [] for i in range(times): log.info( '执行步骤:', self.name, f'第{i+1}轮 并发量: {self.concurrency}' if self.times > 1 else '') threads = [ MyThread(self.run, self.data, self.context) for i in range(self.concurrency) ] [t.start() for t in threads] [t.join() for t in threads] results.extend([t.result for t in threads])
def do_validate(self, validate): """处理断言""" for line in validate: logging.info(f' 处理断言: {line}') if 'comparator' in line: comparator = line.get('comparator') check = line.get('check') expect = line.get('expect') else: comparator, value = tuple(line.items())[0] check, expect = value compare_func = COMPARE_FUNCS.get(comparator) field = self._context.dot_get(check.strip('.')) assert compare_func( field, expect ), f'表达式: {check} 实际结果: {field} not {comparator} 期望结果: {expect}'
def request(data: (list, dict), context): ensure_type(data, (list, dict)) if isinstance(data, list): method, url, *_data = data data = {'method': method, 'url': url} if _data: [data.update(item) for item in _data if isinstance(item, dict)] _session = context._variables.get('_session') base_url = context._variables.get('_base_url') if not _session: _session = requests.Session() context.register_variables({'_session': _session}) _config = context._config if _config: ensure_type(_config, dict) _request_config = _config.get('request') if _request_config: ensure_type(_request_config, dict) if 'base_url' in _request_config: base_url = _request_config.pop('base_url') ensure_type(base_url, str) context.register_variables({'_base_url': base_url}) for key, value in _request_config.items(): try: setattr(_session, key, value) # todo 异常处理 except: logging.warning(f'Session会话不支持设置属性: {key}={value}') url = data.get('url', '') ensure_type(url, str) if base_url and not url.startswith('http'): data['url'] = '/'.join((base_url.rstrip('/'), url.lstrip('/'))) res = _session.request(**data) context.register_variables( { 'content': res.json(), # todo 'status_code': res.status_code } ) logging.info(res.text[:300]) return res
def _run_step_with_concurrency(self, step, times: int, concurrency: int, context): """多轮多线程并发运行""" ensure_type(times, int) ensure_type(concurrency, int) results = [] times = times // concurrency for i in range(times): logging.info(f' 运行Step {step} 第{i + 1}轮 并发数: {concurrency}') results.extend( self._run_step_in_threads(step, concurrency, context)) mod = times % concurrency # 余数 if mod: logging.info(f'运行Step {step} 第{times + 1}轮 并发数: {mod}') results.extend(self._run_step_in_threads(step, mod, context)) return results
def run_suite(self, suite, result): """基础运行suite方法""" log.info('执行测试套件:', suite) topLevel = False if getattr(result, '_testRunEntered', False) is False: result._testRunEntered = topLevel = True for index, test in enumerate(suite): if isnotsuite(test): setup_ok = run_suite_before_case(suite, test, result) if not setup_ok: continue self.run_test(test, result) if suite._cleanup: suite._removeTestAtIndex(index) if topLevel: run_suite_after(suite, result) result._testRunEntered = False return result
def run_suite(self, suite, result, run_func=None, interval=None): """基础运行suite方法,支持指定运行方法""" log.info('执行测试套件:', suite) topLevel = False if getattr(result, '_testRunEntered', False) is False: result._testRunEntered = topLevel = True for index, test in enumerate(suite): if _isnotsuite(test): setup_ok = run_suite_before_case(suite, test, result) if not setup_ok: continue log.info('执行用例:', test.id()) run_func(test, result) if run_func else test(result) # 可能是suite 可能有异常 log.info('执行结果:', test.status, '执行时间:', test.duration) time.sleep(interval) if interval else None if suite._cleanup: suite._removeTestAtIndex(index) if topLevel: run_suite_after(suite, result) result._testRunEntered = False return result
def run_step(self, step: models.Step, context=None): context = context or self._context skip, reason = self._should_skip(step, context) if skip: logging.info(f' 跳过Step {step} {reason}') return times = step._times concurrency = step._concurrency # 1. 无times, 只执行一次 if not times: logging.info(f' 运行Step {step}') return self._run_step(step, context) # 3. 有times, 无concurrency, 顺序执行多轮 if not concurrency: return self._run_step_with_times(step, times, context) # 3. 有times和concurrency return self._run_step_with_concurrency(step, times, concurrency, context)
def wapper(data, context): # todo # tests = parse(tests, context) name = data.get(NAME) extract = data.get(EXTRACT) check = data.get(CHECK) if name: log.info('执行步骤', name) status = 'pass' try: result = func(data, context) except AssertionError as ex: log.exception(ex) return 'fail', None except Exception as ex: log.exception(ex) return 'error', None else: if extract: do_extract(extract, context) if check: status = do_check(check, context) return status, result
def _register_suite(self, suite): if suite._config: logging.info('注册Suite.config') self._context.register_config(suite._config) if suite._variables: # 优先级高于config.variales logging.info('注册Suite.varaible') self._context.register_variables(suite._variables) if suite._keywords: logging.info('注册Suite.keywords') [self._register_keyword(keyword) for keyword in suite._keywords]
def log(data: (str, list, dict), context): if isinstance(data, list): logging.info(*data) # todo else: logging.info(data)
if suite._keywords: logging.info('注册Suite.keywords') [self._register_keyword(keyword) for keyword in suite._keywords] def run_suite(self, suite): self._register_suite(suite) logging.info('运行Suite', suite) for case in suite._cases: self.run_case(case) def run_case(self, case, context=None): context = context or self._context skip, reason = self._should_skip(case, context) if skip: logging.info(f' 跳过Case {case} {reason}') return logging.info(f' 运行Case {case}') for step in case._steps: self.run_step(step) def do_extract(self, extract: list): """处理提取变量""" for line in extract: logging.info(f' 提取变量: {line}') key, expr = tuple(line.items())[0] ensure_type(expr, str) ensure_type(key, str) value = self._context.dot_get(expr) logging.info(f' 注册变量: {key}={value}') self._context.register_variables({key: value})
def run_suite(self, suite): self._register_suite(suite) logging.info('运行Suite', suite) for case in suite._cases: self.run_case(case)
def setUpClass(cls): self._register_suite(suite) logging.info('运行Suite', suite)