def dump_json_file(json_data, pwd_dir_path, dump_file_name): """ dump json data to file """ logs_dir_path = os.path.join(pwd_dir_path, "logs") if not os.path.isdir(logs_dir_path): os.makedirs(logs_dir_path) dump_file_path = os.path.join(logs_dir_path, dump_file_name) try: with io.open(dump_file_path, 'w', encoding='utf-8') as outfile: if is_py2: outfile.write( unicode(json.dumps( json_data, indent=4, separators=(',', ':'), ensure_ascii=False )) ) else: json.dump( json_data, outfile, indent=4, separators=(',', ':'), ensure_ascii=False ) msg = "dump file: {}".format(dump_file_path) logger.color_print(msg, "BLUE") except TypeError: msg = "Failed to dump json file: {}".format(dump_file_path) logger.color_print(msg, "RED")
def create_scaffold(project_path): if os.path.isdir(project_path): folder_name = os.path.basename(project_path) logger.log_warning(u"Folder {} exists, please specify a new folder name.".format(folder_name)) return logger.color_print("Start to create new project: {}\n".format(project_path), "GREEN") def create_path(path, ptype): if ptype == "folder": os.makedirs(path) elif ptype == "file": open(path, 'w').close() return "created {}: {}\n".format(ptype, path) path_list = [ (project_path, "folder"), (os.path.join(project_path, "tests"), "folder"), (os.path.join(project_path, "tests", "api"), "folder"), (os.path.join(project_path, "tests", "suite"), "folder"), (os.path.join(project_path, "tests", "testcases"), "folder"), (os.path.join(project_path, "tests", "debugtalk.py"), "file") ] msg = "" for p in path_list: msg += create_path(p[0], p[1]) logger.color_print(msg, "BLUE")
def prettify_json_file(file_list): """ prettify JSON testset format """ for json_file in set(file_list): if not json_file.endswith(".json"): logger.log_warning("Only JSON file format can be prettified, skip: {}".format(json_file)) continue logger.color_print("Start to prettify JSON file: {}".format(json_file), "GREEN") dir_path = os.path.dirname(json_file) file_name, file_suffix = os.path.splitext(os.path.basename(json_file)) outfile = os.path.join(dir_path, "{}.pretty.json".format(file_name)) with io.open(json_file, 'r', encoding='utf-8') as stream: try: obj = json.load(stream) except ValueError as e: raise SystemExit(e) with io.open(outfile, 'w', encoding='utf-8') as out: json.dump(obj, out, indent=4, separators=(',', ': ')) out.write('\n') print("success: {}".format(outfile))
def dump_json_file(json_data, json_file_abs_path): """ dump json data to file """ class PythonObjectEncoder(json.JSONEncoder): def default(self, obj): try: return super().default(self, obj) except TypeError: return str(obj) try: with io.open(json_file_abs_path, 'w', encoding='utf-8') as outfile: if is_py2: outfile.write( unicode( json.dumps(json_data, indent=4, separators=(',', ':'), ensure_ascii=False, cls=PythonObjectEncoder))) else: json.dump(json_data, outfile, indent=4, separators=(',', ':'), ensure_ascii=False, cls=PythonObjectEncoder) msg = "dump file: {}".format(json_file_abs_path) logger.color_print(msg, "BLUE") except TypeError as ex: msg = "Failed to dump json file: {}\nReason: {}".format( json_file_abs_path, ex) logger.color_print(msg, "RED")
def main_hrun(): """ API test: parse command line options and run commands. """ parser = argparse.ArgumentParser( description='HTTP test runner, not just about api test and load test.') parser.add_argument('-V', '--version', dest='version', action='store_true', help="show version") parser.add_argument('testset_paths', nargs='*', help="testset file path") parser.add_argument( '--html-report-name', help= "specify html report name, only effective when generating html report." ) parser.add_argument('--html-report-template', help="specify html report template path.") parser.add_argument('--log-level', default='INFO', help="Specify logging level, default is INFO.") parser.add_argument( '--dot-env-path', help= "Specify .env file path, which is useful for keeping production credentials." ) parser.add_argument( '--failfast', action='store_true', default=False, help="Stop the test run on the first error or failure.") parser.add_argument('--startproject', help="Specify new project name.") args = parser.parse_args() logger.setup_logger(args.log_level) if args.version: logger.color_print("{}".format(__version__), "GREEN") exit(0) dot_env_path = args.dot_env_path or os.path.join(os.getcwd(), ".env") if dot_env_path: load_dot_env_file(dot_env_path) project_name = args.startproject if project_name: project_path = os.path.join(os.getcwd(), project_name) create_scaffold(project_path) exit(0) result = HttpRunner( args.testset_paths, failfast=args.failfast).run(html_report_name=args.html_report_name) print_output(result["output"]) return 0 if result["success"] else 1
def main_hrun(): """ API test: parse command line options and run commands. """ parser = argparse.ArgumentParser( description='HTTP test runner, not just about api test and load test.') parser.add_argument('-V', '--version', dest='version', action='store_true', help="show version") parser.add_argument('testset_paths', nargs='*', help="testset file path") parser.add_argument('--log-level', default='INFO', help="Specify logging level, default is INFO.") parser.add_argument( '--failfast', action='store_true', default=False, help="Stop the test run on the first error or failure.") parser.add_argument('--startproject', help="Specify new project name.") args = parser.parse_args() logger.setup_logger(args.log_level) args.testset_paths = 'E:\case' if args.version: logger.color_print("HttpRunner version: {}".format(hrun_version), "GREEN") logger.color_print("PyUnitReport version: {}".format(pyu_version), "GREEN") exit(0) project_name = args.startproject if project_name: project_path = os.path.join(os.getcwd(), project_name) create_scaffold(project_path) exit(0) kwargs = { "output": os.path.join(os.getcwd(), "reports"), "failfast": args.failfast } test_runner = HTMLTestRunner(**kwargs) result = run_suite_path(args.testset_paths, {}, test_runner) print_output(result.output) return 0 if result.success else 1
def validate_json_file(file_list): """ validate JSON testcase format """ for json_file in set(file_list): if not json_file.endswith(".json"): logger.log_warning("Only JSON file format can be validated, skip: {}".format(json_file)) continue logger.color_print("Start to validate JSON file: {}".format(json_file), "GREEN") with io.open(json_file) as stream: try: json.load(stream) except ValueError as e: raise SystemExit(e) print("OK")
def validate_json_file(file_list): """ validate JSON testset format """ for json_file in set(file_list): if not json_file.endswith(".json"): logger.log_warning("Only JSON file format can be validated, skip: {}".format(json_file)) continue logger.color_print("Start to validate JSON file: {}".format(json_file), "GREEN") with io.open(json_file) as stream: try: json.load(stream) except ValueError as e: raise SystemExit(e) print("OK")
def parse_locustfile(file_path): """ parse testcase file and return locustfile path. if file_path is a Python file, assume it is a locustfile if file_path is a YAML/JSON file, convert it to locustfile """ if not os.path.isfile(file_path): color_print("file path invalid, exit.", "RED") sys.exit(1) file_suffix = os.path.splitext(file_path)[1] if file_suffix == ".py": locustfile_path = file_path elif file_suffix in ['.yaml', '.yml', '.json']: locustfile_path = gen_locustfile(file_path) else: # '' or other suffix color_print("file type should be YAML/JSON/Python, exit.", "RED") sys.exit(1) return locustfile_path
def parse_locustfile(file_path): """ 解析testcase文件并返回locustfile路径 如果file_path是一个Python文件,则假设它是一个locustfile 如果file_path是YAML/JSON文件,将其转换为locustfile :param file_path: :return: """ if not os.path.isfile(file_path): logger.color_print("文件路径无效,退出.", "RED") sys.exit(1) file_suffix =os.path.splitext(file_path)[1] if file_suffix == ".py": locustfile_path = file_path elif file_suffix in [".yml", ".yaml", ".json"]: locustfile_path = gen_locustfile(file_path) else: logger.color_print("文件类型必须是yml,json,python,exit", "RED") sys.exit(1) return locustfile_path
def parse_locustfile(file_path): """ parse testcase file and return locustfile path. 解析testcase文件并返回locustfile路径 if file_path is a Python file, assume it is a locustfile 如果file_path是一个Python文件,则假设它是一个locustfile if file_path is a YAML/JSON file, convert it to locustfile 如果file_path是YAML/JSON文件,将其转换为locustfile """ if not os.path.isfile(file_path): logger.color_print("file path invalid, exit.", "RED") sys.exit(1) file_suffix = os.path.splitext(file_path)[1] # 文件后缀名 if file_suffix == ".py": locustfile_path = file_path elif file_suffix in ['.yaml', '.yml', '.json']: locustfile_path = gen_locustfile(file_path) #把用例文件转变成py文件 else: # '' or other suffix logger.color_print("file type should be YAML/JSON/Python, exit.", "RED") sys.exit(1) return locustfile_path
import sys from httprunner.cli import main_hrun, main_locust from httprunner.logger import color_print cmd = sys.argv.pop(1) if cmd in ["hrun", "httprunner", "ate"]: main_hrun() elif cmd in ["locust", "locusts"]: main_locust() else: color_print("Miss debugging type.", "RED") example = "\n".join([ "e.g.", "python main-debug.py hrun /path/to/testset_file", "python main-debug.py locusts -f /path/to/testset_file" ]) color_print(example, "yellow")
def main(): """ API test: parse command line options and run commands. """ if is_py2: color_print(get_python2_retire_msg(), "YELLOW") parser = argparse.ArgumentParser(description=__description__) parser.add_argument( '-V', '--version', dest='version', action='store_true', help="show version") parser.add_argument( 'testfile_paths', nargs='*', help="Specify api/testcase/testsuite file paths to run.") parser.add_argument( '--log-level', default='INFO', help="Specify logging level, default is INFO.") parser.add_argument( '--log-file', help="Write logs to specified file path.") parser.add_argument( '--dot-env-path', help="Specify .env file path, which is useful for keeping sensitive data.") parser.add_argument( '--report-template', help="Specify report template path.") parser.add_argument( '--report-dir', help="Specify report save directory.") parser.add_argument( '--report-file', help="Specify report file path, this has higher priority than specifying report dir.") parser.add_argument( '--save-tests', action='store_true', default=False, help="Save loaded/parsed/summary json data to JSON files.") parser.add_argument( '--failfast', action='store_true', default=False, help="Stop the test run on the first error or failure.") parser.add_argument( '--startproject', help="Specify new project name.") parser.add_argument( '--validate', nargs='*', help="Validate JSON testcase format.") parser.add_argument( '--prettify', nargs='*', help="Prettify JSON testcase format.") args = parser.parse_args() if len(sys.argv) == 1: # no argument passed parser.print_help() sys.exit(0) if args.version: color_print("{}".format(__version__), "GREEN") sys.exit(0) if args.validate: validate_json_file(args.validate) sys.exit(0) if args.prettify: prettify_json_file(args.prettify) sys.exit(0) project_name = args.startproject if project_name: create_scaffold(project_name) sys.exit(0) runner = HttpRunner( failfast=args.failfast, save_tests=args.save_tests, log_level=args.log_level, log_file=args.log_file ) err_code = 0 try: for path in args.testfile_paths: summary = runner.run(path, dot_env_path=args.dot_env_path) report_dir = args.report_dir or os.path.join(runner.project_working_directory, "reports") gen_html_report( summary, report_template=args.report_template, report_dir=report_dir, report_file=args.report_file ) err_code |= (0 if summary and summary["success"] else 1) except Exception as ex: color_print("!!!!!!!!!! exception stage: {} !!!!!!!!!!".format(runner.exception_stage), "YELLOW") color_print(str(ex), "RED") capture_exception(ex) err_code = 1 sys.exit(err_code)
def main_hrun(): """ API test: parse command line options and run commands. """ parser = argparse.ArgumentParser(description=__description__) parser.add_argument( '-V', '--version', dest='version', action='store_true', help="show version") parser.add_argument( 'testset_paths', nargs='*', help="testset file path") parser.add_argument( '--no-html-report', action='store_true', default=False, help="do not generate html report.") parser.add_argument( '--html-report-name', help="specify html report name, only effective when generating html report.") parser.add_argument( '--html-report-template', help="specify html report template path.") parser.add_argument( '--log-level', default='INFO', help="Specify logging level, default is INFO.") parser.add_argument( '--log-file', help="Write logs to specified file path.") parser.add_argument( '--dot-env-path', help="Specify .env file path, which is useful for keeping production credentials.") parser.add_argument( '--failfast', action='store_true', default=False, help="Stop the test run on the first error or failure.") parser.add_argument( '--startproject', help="Specify new project name.") parser.add_argument( '--validate', nargs='*', help="Validate JSON testset format.") parser.add_argument( '--prettify', nargs='*', help="Prettify JSON testset format.") args = parser.parse_args() logger.setup_logger(args.log_level, args.log_file) if is_py2: logger.log_warning(get_python2_retire_msg()) if args.version: logger.color_print("{}".format(__version__), "GREEN") exit(0) if args.validate: validate_json_file(args.validate) exit(0) if args.prettify: prettify_json_file(args.prettify) exit(0) project_name = args.startproject if project_name: project_path = os.path.join(os.getcwd(), project_name) create_scaffold(project_path) exit(0) runner = HttpRunner(failfast=args.failfast, dot_env_path=args.dot_env_path).run(args.testset_paths) if not args.no_html_report: runner.gen_html_report( html_report_name=args.html_report_name, html_report_template=args.html_report_template ) summary = runner.summary print_output(summary["output"]) return 0 if summary["success"] else 1
def handle(self, *args, **options): if options['version']: logger.color_print("{}".format(__version__), "GREEN") exit(0)
def runTest(): """ API test: parse command line options and run commands. """ parser = argparse.ArgumentParser( description= 'YDH HTTP test runner, not just about api test and load test.') parser.add_argument('testset_paths', nargs='*', help="testset file path") parser.add_argument( '--html-report-name', help= "specify html report name, only effective when generating html report." ) parser.add_argument('--html-report-template', help="specify html report template path.") parser.add_argument('--log-level', default='INFO', help="Specify logging level, default is INFO.") parser.add_argument( '--env', nargs='*', choices=['develop', 'master', 'release', 'gray', 'production'], help="environment") parser.add_argument('--testcases', nargs='*', help="specified the testcase name to run") parser.add_argument('--target-dirs', nargs='*', help="specified directory to run") args = parser.parse_args() logger.setup_logger(args.log_level) logger.color_print("specified testcase:{}".format(args.testcases)) #若指定参数优先,则优先覆盖为参数值,否则从ini文件中读取 env_list = [] if args.env: Env.init_require_envs(args.env) env_list = args.env else: Env.init_require_envs(Env.target_env_list) env_list = Env.target_env_list logger.color_print("environment:{}".format(env_list)) #解析依赖路径 if not args.testset_paths: logger.log_info("Reading testcase from conf/setting.ini") test_dep_dict, test_path_dict = dependency_parser.load_test_dependency_map_by_path( os.path.join(ROOT_DIR, Config.testcase_root_path, Config.testcase_files_path, Config.testcase_dep_parse_dir)) else: test_dep_dict, test_path_dict = dependency_parser.load_test_dependency_map_by_path( args.testset_paths) logger.log_info("Reading testcase from argument") #提取完整子路径 testcase_dependency = dependency_parser.get_all_dep_paths_with_separator( test_dep_dict, '/') valid_full_testcase_dependency = dependency_parser.extract_full_dep_paths( testcase_dependency) logger.color_print("test_dep_dict(len is {}):\n{}".format( len(test_dep_dict), test_dep_dict)) logger.color_print("test_path_dict(len is {}): \n{}".format( len(test_path_dict), test_path_dict)) logger.color_print("testcase_dependency(len is {}):\n{}".format( len(testcase_dependency), testcase_dependency)) logger.color_print("valid_full_testcase_dependency(len is {}):\n{}".format( len(valid_full_testcase_dependency), valid_full_testcase_dependency)) #生成可视化依赖关系图,包含:1)每条完整最长依赖路径 2)一张图for Overall if Config.debug_output_enable: overall_png = 'Overall.png' reversed_graph_overall = dependency_parser.convert_to_reversed_std_graph( test_dep_dict) dependency_pic_path = os.path.join(ROOT_DIR, Config.debug_root_path, Config.dependency_pic_path) pic_saved_path = os.path.join(dependency_pic_path, overall_png) if not os.path.exists(dependency_pic_path): os.makedirs(dependency_pic_path) dependency_parser.save_graph(reversed_graph_overall, pic_saved_path) std_graph = dependency_parser.get_std_graphs_dict_from_full_path( valid_full_testcase_dependency) for key, graph in std_graph.items(): reversed_graph = dependency_parser.convert_to_reversed_std_graph( graph) file_name = str(key) + '.png' file_path = os.path.join(dependency_pic_path, file_name) dependency_parser.save_graph(reversed_graph, file_path) #依赖根节点测试 testcase_to_root = 'GetToken' login_depend_root = get_login_depend_root(testcase_to_root, valid_full_testcase_dependency) logger.color_print('get_login_depend function test: {} ---> {}'.format( testcase_to_root, login_depend_root)) #从完整子路径中,找出依赖最深的用例名。例如1/2/4,则提取出1 to_run_list = [] test_login_depend_dict = defaultdict(list) for testcase, path in test_path_dict.items(): assert testcase + '.yml' == os.path.basename( path ), 'testcase_name and filename NOT consistent! testcase:{} != file_name:{},\n Check the file in abs_path:{}'.format( testcase, os.path.basename(path), path) for test in valid_full_testcase_dependency: if testcase == test.split('/')[1]: to_run_list.append(test_path_dict[testcase]) if not test_login_depend_dict[testcase]: test_login_depend_dict[testcase] = test.split('/')[-1] else: assert test_login_depend_dict[testcase] == test.split( '/' )[-1], '!!!!!!!Error:One testcase depend on different login' #报错条件:一个用例依赖不同登录视为异常, 如CorpV2LogisticsDeliver,依赖路径上最后的依赖节点只应该为CorpAuthentication logger.color_print( "login_depend_dict is:\n{}".format(test_login_depend_dict)) logger.color_print('testcase list to run(len:{}):\n{}'.format( len(to_run_list), to_run_list)) to_run_set = list(set(to_run_list)) logger.color_print("Remove duplicates(len:{}):\n{}".format( len(to_run_set), to_run_set)) if args.testcases: logger.color_print('The specified testcase to run is:{}'.format( args.testcases)) #切换环境,替换IP for target_env in env_list: Env.reload_env(target_env) ydh_testresult = YDHHtmlTestResult() login_session_dict, global_variables = get_login_session_variabls( test_path_dict, valid_full_dep_path=valid_full_testcase_dependency, test_result=ydh_testresult) logger.color_print( "Login Session Data:\n{}".format(login_session_dict)) http_client_session_dict = dict() if args.testcases: logger.color_print("specified testcases is:{}".format( args.testcases)) if isinstance(args.testcases, (list, set)): # 若参数中指定用例名,则只跑指定的用例 for testcase in args.testcases: run_ydhrunner( testcase=testcase, valid_full_testcase_dependency= valid_full_testcase_dependency, test_path_dict=test_path_dict, http_client_session_dict=http_client_session_dict, test_result=ydh_testresult, global_variables=global_variables) elif args.target_dirs: logger.color_print("specified target_dirs is:{}".format( args.target_dirs)) for target_dir in args.target_dirs: target_dir_files = os.listdir( os.path.join(ROOT_DIR, target_dir)) for test_file in target_dir_files: for test, file_path in test_path_dict.items(): if os.path.basename(file_path) == test_file: run_ydhrunner(testcase=test, valid_full_testcase_dependency= valid_full_testcase_dependency, test_path_dict=test_path_dict, http_client_session_dict= http_client_session_dict, test_result=ydh_testresult, global_variables=global_variables) else: for test_file in to_run_set: for test, file_path in test_path_dict.items(): if file_path == test_file: run_ydhrunner( testcase=test, valid_full_testcase_dependency= valid_full_testcase_dependency, test_path_dict=test_path_dict, http_client_session_dict=http_client_session_dict, test_result=ydh_testresult, global_variables=global_variables) ydh_testresult.render_html_report()
def main(): clean_redis() runTest() logger.color_print("End of main")
def startTest(self, test): """ add start test time """ super(HtmlTestResult, self).startTest(test) logger.color_print(test.shortDescription(), "yellow")