diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 818a173c..16725d43 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -1,11 +1,12 @@ # Release History -## 3.0.2 (2020-04-09) +## 3.0.2 (2020-04-12) **Changed** - replace jsonschema validation with pydantic - remove compatibility with testcase/testsuite format v1 +- make `startproject` as hrun sub-command, usage: `hrun startproject ` ## 3.0.1 (2020-03-24) diff --git a/httprunner/cli.py b/httprunner/cli.py index 2fe9fe41..f84d9af4 100644 --- a/httprunner/cli.py +++ b/httprunner/cli.py @@ -6,64 +6,46 @@ from loguru import logger from httprunner import __description__, __version__ from httprunner.api import HttpRunner +from httprunner.ext.scaffold import init_parser_scaffold, main_scaffold from httprunner.report import gen_html_report -from httprunner.utils import create_scaffold -def main(): - """ 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( +def init_parser_run(subparsers): + sub_parser_run = subparsers.add_parser( + "run", help="Run HttpRunner testcases.") + + sub_parser_run.add_argument( 'testfile_paths', nargs='*', help="Specify api/testcase/testsuite file paths to run.") - parser.add_argument( + sub_parser_run.add_argument( '--log-level', default='INFO', help="Specify logging level, default is INFO.") - parser.add_argument( + sub_parser_run.add_argument( '--log-file', help="Write logs to specified file path.") - parser.add_argument( + sub_parser_run.add_argument( '--dot-env-path', help="Specify .env file path, which is useful for keeping sensitive data.") - parser.add_argument( + sub_parser_run.add_argument( '--report-template', help="Specify report template path.") - parser.add_argument( + sub_parser_run.add_argument( '--report-dir', help="Specify report save directory.") - parser.add_argument( + sub_parser_run.add_argument( '--report-file', help="Specify report file path, this has higher priority than specifying report dir.") - parser.add_argument( + sub_parser_run.add_argument( '--save-tests', action='store_true', default=False, help="Save loaded/parsed/vars_out/summary json data to JSON files.") - parser.add_argument( + sub_parser_run.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() + return sub_parser_run - if len(sys.argv) == 1: - # no argument passed - parser.print_help() - sys.exit(0) - - if args.version: - print(f"{__version__}") - sys.exit(0) - - project_name = args.startproject - if project_name: - create_scaffold(project_name) - sys.exit(0) +def main_run(args): runner = HttpRunner( failfast=args.failfast, save_tests=args.save_tests, @@ -90,5 +72,45 @@ def main(): sys.exit(err_code) +def main(): + """ 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") + + subparsers = parser.add_subparsers(help='sub-command help') + sub_parser_run = init_parser_run(subparsers) + sub_parser_scaffold = init_parser_scaffold(subparsers) + + args = parser.parse_args() + + if args.version: + print(f"{__version__}") + sys.exit(0) + + if len(sys.argv) == 1: + # hrun + parser.print_help() + sys.exit(0) + + elif sys.argv[1] == "run": + # hrun run + if len(sys.argv) == 2: + sub_parser_run.print_help() + sys.exit(0) + + main_run(args) + + elif sys.argv[1] == "startproject": + # hrun startproject + if len(sys.argv) == 2: + sub_parser_scaffold.print_help() + sys.exit(0) + + main_scaffold(args) + + if __name__ == '__main__': main() diff --git a/httprunner/ext/scaffold/__init__.py b/httprunner/ext/scaffold/__init__.py new file mode 100644 index 00000000..2f0fd787 --- /dev/null +++ b/httprunner/ext/scaffold/__init__.py @@ -0,0 +1,129 @@ +import os.path +import sys + +from loguru import logger + + +def init_parser_scaffold(subparsers): + sub_parser_scaffold = subparsers.add_parser( + "startproject", help="Create a new project with template structure.") + sub_parser_scaffold.add_argument("project_name", type=str, nargs="?", help="Specify new project name.") + return sub_parser_scaffold + + +def create_scaffold(project_name): + """ create scaffold with specified project name. + """ + if os.path.isdir(project_name): + logger.warning(f"Folder {project_name} exists, please specify a new folder name.") + return + + logger.info(f"Start to create new project: {project_name}") + logger.info(f"CWD: {os.getcwd()}") + + def create_folder(path): + os.makedirs(path) + msg = f"created folder: {path}" + logger.info(msg) + + def create_file(path, file_content=""): + with open(path, 'w') as f: + f.write(file_content) + msg = f"created file: {path}" + logger.info(msg) + + demo_api_content = """ +name: demo api +variables: + var1: value1 + var2: value2 +request: + url: /api/path/$var1 + method: POST + headers: + Content-Type: "application/json" + json: + key: $var2 +validate: + - eq: ["status_code", 200] +""" + demo_testcase_content = """ +config: + name: "demo testcase" + variables: + device_sn: "ABC" + username: ${ENV(USERNAME)} + password: ${ENV(PASSWORD)} + base_url: "http://127.0.0.1:5000" + +teststeps: +- + name: demo step 1 + api: path/to/api1.yml + variables: + user_agent: 'iOS/10.3' + device_sn: $device_sn + extract: + token: content.token + validate: + - eq: ["status_code", 200] +- + name: demo step 2 + api: path/to/api2.yml + variables: + token: $token +""" + demo_testsuite_content = """ +config: + name: "demo testsuite" + variables: + device_sn: "XYZ" + base_url: "http://127.0.0.1:5000" + +testcases: +- + name: call demo_testcase with data 1 + testcase: path/to/demo_testcase.yml + variables: + device_sn: $device_sn +- + name: call demo_testcase with data 2 + testcase: path/to/demo_testcase.yml + variables: + device_sn: $device_sn +""" + ignore_content = "\n".join([ + ".env", + "reports/*", + "__pycache__/*", + "*.pyc", + ".python-version", + "logs/*" + ]) + demo_debugtalk_content = """ +import time + +def sleep(n_secs): + time.sleep(n_secs) +""" + demo_env_content = "\n".join([ + "USERNAME=leolee", + "PASSWORD=123456" + ]) + + create_folder(project_name) + create_folder(os.path.join(project_name, "api")) + create_folder(os.path.join(project_name, "testcases")) + create_folder(os.path.join(project_name, "testsuites")) + create_folder(os.path.join(project_name, "reports")) + create_file(os.path.join(project_name, "api", "demo_api.yml"), demo_api_content) + create_file(os.path.join(project_name, "testcases", "demo_testcase.yml"), demo_testcase_content) + create_file(os.path.join(project_name, "testsuites", "demo_testsuite.yml"), demo_testsuite_content) + create_file(os.path.join(project_name, "debugtalk.py"), demo_debugtalk_content) + create_file(os.path.join(project_name, ".env"), demo_env_content) + create_file(os.path.join(project_name, ".gitignore"), ignore_content) + + +def main_scaffold(args): + create_scaffold(args.project_name) + sys.exit(0) diff --git a/httprunner/ext/scaffold/test_scaffold.py b/httprunner/ext/scaffold/test_scaffold.py new file mode 100644 index 00000000..6f53a213 --- /dev/null +++ b/httprunner/ext/scaffold/test_scaffold.py @@ -0,0 +1,19 @@ +import os +import shutil +import unittest + +from httprunner.ext.scaffold import create_scaffold + + +class TestUtils(unittest.TestCase): + + def test_create_scaffold(self): + project_name = "projectABC" + create_scaffold(project_name) + self.assertTrue(os.path.isdir(os.path.join(project_name, "api"))) + self.assertTrue(os.path.isdir(os.path.join(project_name, "testcases"))) + self.assertTrue(os.path.isdir(os.path.join(project_name, "testsuites"))) + self.assertTrue(os.path.isdir(os.path.join(project_name, "reports"))) + self.assertTrue(os.path.isfile(os.path.join(project_name, "debugtalk.py"))) + self.assertTrue(os.path.isfile(os.path.join(project_name, ".env"))) + shutil.rmtree(project_name) diff --git a/httprunner/utils.py b/httprunner/utils.py index e9c4cafa..5d8601b2 100644 --- a/httprunner/utils.py +++ b/httprunner/utils.py @@ -349,119 +349,6 @@ def print_info(info_mapping): logger.info(content) -def create_scaffold(project_name): - """ create scaffold with specified project name. - """ - if os.path.isdir(project_name): - logger.warning(f"Folder {project_name} exists, please specify a new folder name.") - return - - logger.info(f"Start to create new project: {project_name}") - logger.info(f"CWD: {os.getcwd()}") - - def create_folder(path): - os.makedirs(path) - msg = f"created folder: {path}" - logger.info(msg) - - def create_file(path, file_content=""): - with open(path, 'w') as f: - f.write(file_content) - msg = f"created file: {path}" - logger.info(msg) - - demo_api_content = """ -name: demo api -variables: - var1: value1 - var2: value2 -request: - url: /api/path/$var1 - method: POST - headers: - Content-Type: "application/json" - json: - key: $var2 -validate: - - eq: ["status_code", 200] -""" - demo_testcase_content = """ -config: - name: "demo testcase" - variables: - device_sn: "ABC" - username: ${ENV(USERNAME)} - password: ${ENV(PASSWORD)} - base_url: "http://127.0.0.1:5000" - -teststeps: -- - name: demo step 1 - api: path/to/api1.yml - variables: - user_agent: 'iOS/10.3' - device_sn: $device_sn - extract: - - token: content.token - validate: - - eq: ["status_code", 200] -- - name: demo step 2 - api: path/to/api2.yml - variables: - token: $token -""" - demo_testsuite_content = """ -config: - name: "demo testsuite" - variables: - device_sn: "XYZ" - base_url: "http://127.0.0.1:5000" - -testcases: -- - name: call demo_testcase with data 1 - testcase: path/to/demo_testcase.yml - variables: - device_sn: $device_sn -- - name: call demo_testcase with data 2 - testcase: path/to/demo_testcase.yml - variables: - device_sn: $device_sn -""" - ignore_content = "\n".join([ - ".env", - "reports/*", - "__pycache__/*", - "*.pyc", - ".python-version", - "logs/*" - ]) - demo_debugtalk_content = """ -import time - -def sleep(n_secs): - time.sleep(n_secs) -""" - demo_env_content = "\n".join([ - "USERNAME=leolee", - "PASSWORD=123456" - ]) - - create_folder(project_name) - create_folder(os.path.join(project_name, "api")) - create_folder(os.path.join(project_name, "testcases")) - create_folder(os.path.join(project_name, "testsuites")) - create_folder(os.path.join(project_name, "reports")) - create_file(os.path.join(project_name, "api", "demo_api.yml"), demo_api_content) - create_file(os.path.join(project_name, "testcases", "demo_testcase.yml"), demo_testcase_content) - create_file(os.path.join(project_name, "testsuites", "demo_testsuite.yml"), demo_testsuite_content) - create_file(os.path.join(project_name, "debugtalk.py"), demo_debugtalk_content) - create_file(os.path.join(project_name, ".env"), demo_env_content) - create_file(os.path.join(project_name, ".gitignore"), ignore_content) - - def gen_cartesian_product(*args): """ generate cartesian product for lists diff --git a/httprunner/utils_test.py b/httprunner/utils_test.py index e41761d7..d37dce91 100644 --- a/httprunner/utils_test.py +++ b/httprunner/utils_test.py @@ -1,6 +1,5 @@ import io import os -import shutil import unittest from httprunner import exceptions, loader, utils @@ -210,17 +209,6 @@ class TestUtils(unittest.TestCase): self.assertEqual(id(new_data["c"]), id(data["c"])) # self.assertEqual(id(new_data["d"]), id(data["d"])) - def test_create_scaffold(self): - project_name = "projectABC" - utils.create_scaffold(project_name) - self.assertTrue(os.path.isdir(os.path.join(project_name, "api"))) - self.assertTrue(os.path.isdir(os.path.join(project_name, "testcases"))) - self.assertTrue(os.path.isdir(os.path.join(project_name, "testsuites"))) - self.assertTrue(os.path.isdir(os.path.join(project_name, "reports"))) - self.assertTrue(os.path.isfile(os.path.join(project_name, "debugtalk.py"))) - self.assertTrue(os.path.isfile(os.path.join(project_name, ".env"))) - shutil.rmtree(project_name) - def test_cartesian_product_one(self): parameters_content_list = [ [