def run_tests(self, tests_mapping): """ run testcase/testsuite data """ capture_message("start to run tests") project_mapping = tests_mapping.get("project_mapping", {}) self.project_working_directory = project_mapping.get( "PWD", os.getcwd()) if self.save_tests: utils.dump_logs(tests_mapping, project_mapping, "loaded") # parse tests self.exception_stage = "parse tests" parsed_testcases = parser.parse_tests(tests_mapping) parse_failed_testfiles = parser.get_parse_failed_testfiles() if parse_failed_testfiles: logger.log_warning("parse failures occurred ...") utils.dump_logs(parse_failed_testfiles, project_mapping, "parse_failed") if self.save_tests: utils.dump_logs(parsed_testcases, project_mapping, "parsed") # add tests to test suite self.exception_stage = "add tests to test suite" test_suite = self._add_tests(parsed_testcases) # run test suite self.exception_stage = "run test suite" results = self._run_suite(test_suite) # aggregate results self.exception_stage = "aggregate results" self._summary = self._aggregate(results) # generate html report self.exception_stage = "generate html report" report.stringify_summary(self._summary) if self.save_tests: utils.dump_logs(self._summary, project_mapping, "summary") # save variables and export data vars_out = self.get_vars_out() utils.dump_logs(vars_out, project_mapping, "vars_out") return self._summary
def test_parse_tests_variable_not_found(self): from tests.debugtalk import sum_two tests_mapping = { "project_mapping": { "functions": { "sum_two": sum_two } }, 'testcases': [ { "config": { 'name': '', "base_url": "$host1", 'variables': { "host1": "https://debugtalk.com" } }, "teststeps": [ { 'name': 'testcase1', "base_url": "$host2", "variables": { "host2": "https://httprunner.org", "num4": "${sum_two($num0, 5)}", "num3": "${sum_two($num2, 4)}", "num2": "${sum_two($num1, 3)}", "num1": "${sum_two(1, 2)}" }, 'request': { 'url': '/api1/?num1=$num1&num2=$num2&num3=$num3&num4=$num4', 'method': 'GET' } } ] } ] } parsed_tests_mapping = parser.parse_tests(tests_mapping) test_dict = parsed_tests_mapping["testcases"][0]["teststeps"][0] self.assertEqual(test_dict["variables"]["num3"], 10) self.assertEqual(test_dict["variables"]["num2"], 6) self.assertEqual(test_dict["variables"]["num4"], "${sum_two($num0, 5)}") self.assertEqual( test_dict["request"]["url"], "https://httprunner.org/api1/?num1=$num1&num2=$num2&num3=$num3&num4=$num4" )
def test_testcase_parser(self): testcase_path = "tests/testcases/setup.yml" tests_mapping = loader.load_tests(testcase_path) parsed_tests_mapping = parser.parse_tests(tests_mapping) parsed_testcases = parsed_tests_mapping["testcases"] self.assertEqual(len(parsed_testcases), 1) self.assertNotIn("variables", parsed_testcases[0]["config"]) self.assertEqual(len(parsed_testcases[0]["teststeps"]), 2) test_dict1 = parsed_testcases[0]["teststeps"][0] self.assertEqual(test_dict1["name"], "get token (setup)") self.assertNotIn("api_def", test_dict1) self.assertEqual(test_dict1["variables"]["device_sn"], "TESTCASE_SETUP_XXX") self.assertEqual(test_dict1["request"]["url"], "http://127.0.0.1:5000/api/get-token")
def test_parse_tests(self): testcase_file_path = os.path.join( os.getcwd(), 'tests/data/demo_testcase.yml') testcases = loader.load_tests(testcase_file_path) parsed_testcases = parser.parse_tests(testcases) self.assertEqual(parsed_testcases[0]["config"]["variables"]["var_c"], 3) self.assertEqual(len(parsed_testcases), 2 * 2) self.assertEqual( parsed_testcases[0]["config"]["request"]["base_url"], '$BASE_URL' ) self.assertEqual( parsed_testcases[0]["config"]["variables"]["BASE_URL"], 'http://127.0.0.1:5000' ) self.assertIsInstance(parsed_testcases, list) self.assertEqual(parsed_testcases[0]["config"]["name"], '12311')
def test_run_testcase_with_teardown_hooks_success(self): testcases = [ { "config": { "name": "basic test with httpbin" }, "teststeps": [ { "name": "get token", "request": { "url": "http://127.0.0.1:5000/api/get-token", "method": "POST", "headers": { "content-type": "application/json", "user_agent": "iOS/10.3", "device_sn": "HZfFBh6tU59EdXJ", "os_platform": "ios", "app_version": "2.8.6" }, "json": { "sign": "5188962c489d1a35effa99e9346dd5efd4fdabad" } }, "validate": [ {"check": "status_code", "expect": 200} ], "teardown_hooks": ["${teardown_hook_sleep_N_secs($response, 2)}"] } ] } ] tests_mapping = { "project_mapping": { "functions": self.debugtalk_functions }, "testcases": testcases } parsed_testcases = parser.parse_tests(tests_mapping) parsed_testcase = parsed_testcases[0] test_runner = runner.Runner(parsed_testcase["config"]) start_time = time.time() test_runner.run_test(parsed_testcase["teststeps"][0]) end_time = time.time() # check if teardown function executed self.assertLess(end_time - start_time, 0.5)
def test_testsuite_parser(self): testcase_path = "tests/testsuites/create_users.yml" tests_mapping = loader.load_cases(testcase_path) parsed_testcases = parser.parse_tests(tests_mapping) self.assertEqual(len(parsed_testcases), 2) self.assertEqual(len(parsed_testcases[0]["teststeps"]), 2) testcase1 = parsed_testcases[0]["teststeps"][0] self.assertIn("setup and reset all (override)", testcase1["config"]["name"].raw_string) teststeps = testcase1["teststeps"] self.assertNotIn("testcase_def", testcase1) self.assertEqual(len(teststeps), 2) self.assertEqual( teststeps[0]["request"]["url"], "/api/get-token" )
def test_testsuite_run_suite(self): testcase_path = "tests/testsuites/create_users.yml" tests_mapping = loader.load_cases(testcase_path) testcases = parser.parse_tests(tests_mapping) runner = HttpRunner() test_suite = runner._add_tests(testcases) tests_results = runner._run_suite(test_suite) self.assertEqual(len(tests_results[0][1].records), 2) results = tests_results[0][1] self.assertIn("setup and reset all (override)", results.records[0]["name"]) self.assertEqual(results.records[1]["name"], "create user and check result.")
def test_validate(self): testcases = [{ "config": { 'name': "test validation" }, "teststeps": [{ "name": "test validation", "request": { "url": "http://127.0.0.1:5000/", "method": "GET", }, "variables": { "resp_status_code": 200, "resp_body_success": True }, "validate": [{ "eq": ["$resp_status_code", 200] }, { "check": "$resp_status_code", "comparator": "eq", "expect": 200 }, { "check": "$resp_body_success", "expect": True }, { "check": "${is_status_code_200($resp_status_code)}", "expect": True }] }] }] from tests.debugtalk import is_status_code_200 tests_mapping = { "project_mapping": { "functions": { "is_status_code_200": is_status_code_200 } }, "testcases": testcases } testcases = parser.parse_tests(tests_mapping) parsed_testcase = testcases[0] test_runner = runner.Runner(parsed_testcase["config"]) teststep = parsed_testcase["teststeps"][0] test_runner.run_test(teststep)
def test_parse_tests_variable_with_function(self): from tests.debugtalk import sum_two, gen_random_string tests_mapping = { "project_mapping": { "functions": { "sum_two": sum_two, "gen_random_string": gen_random_string } }, 'testcases': [{ "config": { 'name': '', "base_url": "$host1", 'variables': { "host1": "https://debugtalk.com", "var_a": "${gen_random_string(5)}", "var_b": "$var_a" } }, "teststeps": [{ 'name': 'testcase1', "base_url": "$host2", "variables": { "host2": "https://httprunner.org", "num3": "${sum_two($num2, 4)}", "num2": "${sum_two($num1, 3)}", "num1": "${sum_two(1, 2)}", "str1": "${gen_random_string(5)}", "str2": "$str1" }, 'request': { 'url': '/api1/?num1=$num1&num2=$num2&num3=$num3', 'method': 'GET' } }] }] } parsed_tests_mapping = parser.parse_tests(tests_mapping) test_dict = parsed_tests_mapping["testcases"][0]["teststeps"][0] self.assertEqual(test_dict["variables"]["num3"], 10) self.assertEqual(test_dict["variables"]["num2"], 6) self.assertEqual(test_dict["variables"]["str1"], test_dict["variables"]["str2"]) self.assertEqual(test_dict["request"]["url"], "https://httprunner.org/api1/?num1=3&num2=6&num3=10")
def test_parse_tests_testcase(self): testcase_file_path = os.path.join(os.getcwd(), 'tests/data/demo_testcase.yml') tests_mapping = loader.load_tests(testcase_file_path) testcases = tests_mapping["testcases"] self.assertEqual(testcases[0]["config"]["variables"]["var_c"], "${sum_two(1, 2)}") self.assertEqual(testcases[0]["config"]["variables"]["PROJECT_KEY"], "${ENV(PROJECT_KEY)}") parsed_tests_mapping = parser.parse_tests(tests_mapping) parsed_testcases = parsed_tests_mapping["testcases"] self.assertIsInstance(parsed_testcases, list) test_dict1 = parsed_testcases[0]["teststeps"][0] self.assertEqual(test_dict1["variables"]["var_c"], 3) self.assertEqual(test_dict1["variables"]["PROJECT_KEY"], "ABCDEFGH") self.assertEqual(test_dict1["variables"]["var_d"], test_dict1["variables"]["var_e"]) self.assertEqual(parsed_testcases[0]["config"]["name"], '1230')
def test_testcase_complex_run_suite(self): testcase_path = "tests/testcases/create_and_check.yml" tests_mapping = loader.load_tests(testcase_path) parsed_tests_mapping = parser.parse_tests(tests_mapping) runner = HttpRunner() test_suite = runner._add_tests(parsed_tests_mapping) tests_results = runner._run_suite(test_suite) self.assertEqual(len(tests_results[0][1].records), 4) results = tests_results[0][1] self.assertEqual( results.records[0]["name"], "setup and reset all (override) for TESTCASE_CREATE_XXX." ) self.assertEqual( results.records[1]["name"], "make sure user 9001 does not exist" )
def test_parse_tests_testcase(self): testcase_file_path = os.path.join(os.getcwd(), 'tests/data/demo_testcase.yml') tests_mapping = loader.load_tests(testcase_file_path) testcases = tests_mapping["testcases"] self.assertEqual(testcases[0]["config"]["variables"]["var_c"], "${sum_two($var_a, $var_b)}") self.assertEqual(testcases[0]["config"]["variables"]["PROJECT_KEY"], "${ENV(PROJECT_KEY)}") parsed_testcases = parser.parse_tests(tests_mapping) self.assertIsInstance(parsed_testcases, list) test_dict1 = parsed_testcases[0]["teststeps"][0] self.assertEqual(test_dict1["variables"]["var_c"].raw_string, "${sum_two($var_a, $var_b)}") self.assertEqual(test_dict1["variables"]["PROJECT_KEY"].raw_string, "${ENV(PROJECT_KEY)}") self.assertIsInstance(parsed_testcases[0]["config"]["name"], parser.LazyString)
def test_run_testcase_with_hooks_modify_request(self): testcases = [ { "config": { "name": "basic test with httpbin", "base_url": HTTPBIN_SERVER }, "teststeps": [ { "name": "modify request headers", "base_url": HTTPBIN_SERVER, "request": { "url": "/anything", "method": "POST", "headers": { "content-type": "application/json", "user_agent": "iOS/10.3" }, "json": { "os_platform": "ios", "sign": "5188962c489d1a35effa99e9346dd5efd4fdabad" } }, "setup_hooks": [ "${modify_request_json($request, android)}" ], "validate": [ {"check": "status_code", "expect": 200}, {"check": "content.json.os_platform", "expect": "android"} ] } ] } ] tests_mapping = { "project_mapping": { "functions": self.debugtalk_functions }, "testcases": testcases } parsed_testcases = parser.parse_tests(tests_mapping) parsed_testcase = parsed_testcases[0] test_runner = runner.Runner(parsed_testcase["config"]) test_runner.run_test(parsed_testcase["teststeps"][0])
def test_testsuite_parser(self): testcase_path = "tests/testsuites/create_users.yml" tests_mapping = loader.load_tests(testcase_path) parsed_tests_mapping = parser.parse_tests(tests_mapping) parsed_testcases = parsed_tests_mapping["testcases"] self.assertEqual(len(parsed_testcases), 2) self.assertEqual(len(parsed_testcases[0]["teststeps"]), 4) testcase1 = parsed_testcases[0]["teststeps"][0] self.assertIn("setup and reset all (override)", testcase1["config"]["name"]) self.assertNotIn("testcase_def", testcase1) self.assertEqual(len(testcase1["teststeps"]), 2) self.assertEqual( testcase1["teststeps"][0]["request"]["url"], "http://127.0.0.1:5000/api/get-token" ) self.assertEqual(len(testcase1["teststeps"][0]["variables"]["device_sn"]), 15)
def test_testsuite_run_suite(self): testcase_path = "tests/testsuites/create_users.yml" tests_mapping = loader.load_tests(testcase_path) parsed_tests_mapping = parser.parse_tests(tests_mapping) runner = HttpRunner() test_suite = runner._add_tests(parsed_tests_mapping) tests_results = runner._run_suite(test_suite) self.assertEqual(len(tests_results[0][1].records), 4) results = tests_results[0][1] self.assertIn("setup and reset all (override)", results.records[0]["name"]) self.assertIn(results.records[1]["name"], [ "make sure user 1000 does not exist", "make sure user 1001 does not exist" ])
def test_run_testcase_with_hooks_assignment(self): testcases = [ { "config": { "name": "basic test with httpbin", "base_url": HTTPBIN_SERVER }, "teststeps": [ { "name": "modify request headers", "base_url": HTTPBIN_SERVER, "request": { "url": "/anything", "method": "POST", "headers": { "user_agent": "iOS/10.3", "os_platform": "ios" }, "data": "a=1&b=2" }, "setup_hooks": [ {"total": "${sum_two(1, 5)}"} ], "validate": [ {"check": "status_code", "expect": 200} ] } ] } ] tests_mapping = { "project_mapping": { "functions": self.debugtalk_functions }, "testcases": testcases } parsed_testcases = parser.parse_tests(tests_mapping) parsed_testcase = parsed_testcases[0] test_runner = runner.Runner(parsed_testcase["config"]) test_runner.run_test(parsed_testcase["teststeps"][0]) test_variables_mapping = test_runner.session_context.test_variables_mapping self.assertEqual(test_variables_mapping["total"], 6) self.assertEqual(test_variables_mapping["request"]["data"], "a=1&b=2")
def test_run_validate_elapsed(self): testcases = [ { "config": {}, "teststeps": [ { "name": "get token", "request": { "url": "http://127.0.0.1:5000/api/get-token", "method": "POST", "headers": { "content-type": "application/json", "user_agent": "iOS/10.3", "device_sn": "HZfFBh6tU59EdXJ", "os_platform": "ios", "app_version": "2.8.6" }, "json": { "sign": "5188962c489d1a35effa99e9346dd5efd4fdabad" } }, "validate": [ {"check": "status_code", "expect": 200}, {"check": "elapsed.seconds", "comparator": "lt", "expect": 1}, {"check": "elapsed.days", "comparator": "eq", "expect": 0}, {"check": "elapsed.microseconds", "comparator": "gt", "expect": 1000}, {"check": "elapsed.total_seconds", "comparator": "lt", "expect": 1} ] } ] } ] tests_mapping = { "project_mapping": { "functions": self.debugtalk_functions }, "testcases": testcases } parsed_testcases = parser.parse_tests(tests_mapping) parsed_testcase = parsed_testcases[0] test_runner = runner.Runner(parsed_testcase["config"]) test_runner.run_test(parsed_testcase["teststeps"][0])
def test_run_testcase_with_parameters_name(self): testcase_file_path = os.path.join(os.getcwd(), 'tests/data/demo_parameters.yml') testcases = loader.load_tests(testcase_file_path) parsed_testcases = parser.parse_tests(testcases) runner = HttpRunner() test_suite = runner._add_tests(parsed_testcases) self.assertEqual(test_suite._tests[0].teststeps[0]['name'], 'get token with iOS/10.1 and test1') self.assertEqual(test_suite._tests[1].teststeps[0]['name'], 'get token with iOS/10.1 and test2') self.assertEqual(test_suite._tests[2].teststeps[0]['name'], 'get token with iOS/10.2 and test1') self.assertEqual(test_suite._tests[3].teststeps[0]['name'], 'get token with iOS/10.2 and test2') self.assertEqual(test_suite._tests[4].teststeps[0]['name'], 'get token with iOS/10.3 and test1') self.assertEqual(test_suite._tests[5].teststeps[0]['name'], 'get token with iOS/10.3 and test2')
def test_testcase_complex_run_suite(self): for testcase_path in [ "tests/testcases/create_user.yml", "tests/testcases/create_user.v2.yml", "tests/testcases/create_user.json", "tests/testcases/create_user.v2.json" ]: tests_mapping = loader.load_cases(testcase_path) testcases = parser.parse_tests(tests_mapping) runner = HttpRunner() test_suite = runner._add_tests(testcases) tests_results = runner._run_suite(test_suite) self.assertEqual(len(tests_results[0][1].records), 2) results = tests_results[0][1] self.assertEqual( results.records[0]["name"], "setup and reset all (override) for TESTCASE_CREATE_XXX.") self.assertEqual(results.records[1]["name"], "create user and check result.")
def test_parse_tests_verify_config_unset(self): """ verify priority: test_dict > config """ tests_mapping = { 'testcases': [{ "config": { 'name': 'bugfix verify', "base_url": "https://httpbin.org/", }, "teststeps": [{ 'name': 'testcase1', 'request': { 'url': '/headers', 'method': 'GET' } }] }] } parsed_testcases = parser.parse_tests(tests_mapping) test_dict = parsed_testcases[0]["teststeps"][0] self.assertEqual(test_dict["request"]["verify"], True)
def run_tests(self, tests_mapping): """ run testcase/testsuite data """ project_mapping = tests_mapping.get("project_mapping", {}) if self.save_tests: utils.dump_logs(tests_mapping, project_mapping, "loaded") # parse tests self.exception_stage = "parse tests" parsed_testcases = parser.parse_tests(tests_mapping) if self.save_tests: utils.dump_logs(parsed_testcases, project_mapping, "parsed") # add tests to test suite self.exception_stage = "add tests to test suite" test_suite = self._add_tests(parsed_testcases) # run test suite self.exception_stage = "run test suite" results = self._run_suite(test_suite) # aggregate results self.exception_stage = "aggregate results" self._summary = self._aggregate(results) # generate html report self.exception_stage = "generate html report" report.stringify_summary(self._summary) if self.save_tests: utils.dump_logs(self._summary, project_mapping, "summary") report_path = report.render_html_report( self._summary, self.report_template, self.report_dir ) return report_path
def run_tests(self, tests_mapping): """ run testcase/testsuite data """ capture_message("start to run tests") project_mapping = tests_mapping.get("project_mapping", {}) self.project_working_directory = project_mapping.get( "PWD", os.getcwd()) if self.save_tests: utils.dump_logs(tests_mapping, project_mapping, "loaded") # parse tests self.exception_stage = "parse tests" parsed_testcases = parser.parse_tests(tests_mapping) if self.save_tests: utils.dump_logs(parsed_testcases, project_mapping, "parsed") # add tests to test suite self.exception_stage = "add tests to test suite" test_suite = self._add_tests(parsed_testcases) # run test suite self.exception_stage = "run test suite" results = self._run_suite(test_suite) # aggregate results self.exception_stage = "aggregate results" self._summary = self._aggregate(results) # generate html report self.exception_stage = "generate html report" report.stringify_summary(self._summary) if self.save_tests: utils.dump_logs(self._summary, project_mapping, "summary") return self._summary
def prepare_locust_tests(path): """ 准备蝗虫的测试 :param path:testcase file path :return: list: locust tests data [ testcase1_dict, testcase2_dict ] """ tests_mapping = loader.load_cases(path) testcases = parser.parse_tests(tests_mapping) locust_tests = [] for testcase in testcases: testcase_weight = testcase.get("config", {}).pop("weight", 1) for _ in range(testcase_weight): locust_tests.append(testcase) return locust_tests
def run_tests(self, tests_mapping): """ run testcase/testsuite data """ capture_message("start to run tests") self.test_path = tests_mapping.get("project_mapping", {}).get("test_path", "") if self.save_tests: utils.dump_json_file( tests_mapping, utils.prepare_log_file_abs_path(self.test_path, "loaded.json") ) # parse tests self.exception_stage = "parse tests" parsed_testcases = parser.parse_tests(tests_mapping) parse_failed_testfiles = parser.get_parse_failed_testfiles() if parse_failed_testfiles: logger.warning("parse failures occurred ...") utils.dump_json_file( parse_failed_testfiles, utils.prepare_log_file_abs_path(self.test_path, "parse_failed.json") ) if len(parsed_testcases) == 0: logger.error("failed to parse all cases, abort.") raise exceptions.ParseTestsFailure if self.save_tests: utils.dump_json_file( parsed_testcases, utils.prepare_log_file_abs_path(self.test_path, "parsed.json") ) # add tests to test suite self.exception_stage = "add tests to test suite" test_suite = self._add_tests(parsed_testcases) # run test suite self.exception_stage = "run test suite" results = self._run_suite(test_suite) # aggregate results self.exception_stage = "aggregate results" self._summary = self._aggregate(results) # generate html report self.exception_stage = "generate html report" report.stringify_summary(self._summary) if self.save_tests: utils.dump_json_file( self._summary, utils.prepare_log_file_abs_path(self.test_path, "summary.json") ) # save variables and export data vars_out = self.get_vars_out() utils.dump_json_file( vars_out, utils.prepare_log_file_abs_path(self.test_path, "io.json") ) return self._summary
def run_tests(self, tests_mapping): """ run testcase/testsuite data project_mapping = { "env": {"username": "******","password": "******"}, "PWD": "D:\\git_ligeit\\test_ucong", "functions": {}, "test_path": "D:\\git_ligeit\\test_ucong\\api\\财务管理\\服务中心银行流水\\汇款.yml" } parsed_testcases = [ { "config":{ "name":"/mgmt/store/checkBusinessAddressIsExist" }, "teststeps":[ { "name":"/mgmt/store/checkBusinessAddressIsExist", "request":{ "headers":{ "Authorization":"LazyString(${token_type} ${access_token})" }, "method":"GET", "params":{ "provinceName":"LazyString(${provinceName})", "cityName":"LazyString(${cityName})", "areaName":"LazyString(${areaName})", "streetName":"LazyString(${streetName})", "detailAddress":"LazyString(${detailAddress})" }, "url":"LazyString(${base_url}/mgmt/store/checkBusinessAddressIsExist)", "verify":true }, "variables":{ "provinceName":"广东省", "cityName":"广州市", "areaName":"海珠区", "streetName":"南州街道", "detailAddress":"广州市海珠区南洲街道新滘中路88号唯品同创汇6区东三街17号自编23号", "access_token":"LazyString(${ENV(access_token)})", "token_type":"LazyString(${ENV(token_type)})", "base_url":"LazyString(${ENV(base_url)})" }, "validate":[ "LazyFunction(equals(status_code, 200))" ] } ] } ] """ capture_message("start to run tests") project_mapping = tests_mapping.get("project_mapping", {}) self.project_working_directory = project_mapping.get( "PWD", os.getcwd()) #项目工作目录 if self.save_tests: utils.dump_logs(tests_mapping, project_mapping, "loaded") #转储日志 # parse tests self.exception_stage = "parse tests" # 用测试用例抽出来(变量已经按优先级处理正确),剩下LazyString(${token_type} ${access_token})未解析 parsed_testcases = parser.parse_tests(tests_mapping) parse_failed_testfiles = parser.get_parse_failed_testfiles() if parse_failed_testfiles: logger.log_warning("parse failures occurred ...") utils.dump_logs(parse_failed_testfiles, project_mapping, "parse_failed") if len(parsed_testcases) == 0: logger.log_error("failed to parse all cases, abort.") raise exceptions.ParseTestsFailure if self.save_tests: utils.dump_logs(parsed_testcases, project_mapping, "parsed") # add tests to test suite self.exception_stage = "add tests to test suite" # <unittest.suite.TestSuite tests= # [<unittest.suite.TestSuite tests=[<httprunner.api.TestSequense testMethod=test_0000_000>]>]> test_suite = self._add_tests(parsed_testcases) # run test suite self.exception_stage = "run test suite" results = self._run_suite(test_suite) # aggregate results总结果 self.exception_stage = "aggregate results" self._summary = self._aggregate(results) # generate html report self.exception_stage = "generate html report" report.stringify_summary(self._summary) if self.save_tests: utils.dump_logs(self._summary, project_mapping, "summary") # save variables and export data保存变量和导出数据 vars_out = self.get_vars_out() utils.dump_logs(vars_out, project_mapping, "io") return self._summary