diff --git a/examples/postman_echo/conftest.py b/examples/postman_echo/conftest.py new file mode 100644 index 00000000..9bb834e6 --- /dev/null +++ b/examples/postman_echo/conftest.py @@ -0,0 +1,62 @@ +# NOTICE: Generated By HttpRunner. +import json +import os +import time + +import pytest +from loguru import logger + +from httprunner.utils import get_platform + + +@pytest.fixture(scope="session", autouse=True) +def session_fixture(request): + """setup and teardown each task""" + logger.info(f"start running testcases ...") + + start_at = time.time() + + yield + + logger.info(f"task finished, generate task summary for --save-tests") + + summary = { + "success": True, + "stat": { + "testcases": {"total": 0, "success": 0, "fail": 0}, + "teststeps": {"total": 0, "failures": 0, "successes": 0}, + }, + "time": {"start_at": start_at, "duration": time.time() - start_at}, + "platform": get_platform(), + "details": [], + } + + for item in request.node.items: + testcase_summary = item.instance.get_summary() + summary["success"] &= testcase_summary.success + + summary["stat"]["testcases"]["total"] += 1 + summary["stat"]["teststeps"]["total"] += len(testcase_summary.step_datas) + if testcase_summary.success: + summary["stat"]["testcases"]["success"] += 1 + summary["stat"]["teststeps"]["successes"] += len( + testcase_summary.step_datas + ) + else: + summary["stat"]["testcases"]["fail"] += 1 + summary["stat"]["teststeps"]["successes"] += ( + len(testcase_summary.step_datas) - 1 + ) + summary["stat"]["teststeps"]["failures"] += 1 + + summary["details"].append(testcase_summary.dict()) + + summary_path = "/Users/debugtalk/MyProjects/HttpRunner-dev/HttpRunner/examples/postman_echo/logs/request_methods/hardcode.summary.json" + summary_dir = os.path.dirname(summary_path) + os.makedirs(summary_dir, exist_ok=True) + + with open(summary_path, "w", encoding="utf-8") as f: + json.dump(summary, f, indent=4) + + logger.info(f"generated task summary: {summary_path}") + diff --git a/examples/postman_echo/cookie_manipulation/__init__.py b/examples/postman_echo/cookie_manipulation/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/examples/postman_echo/cookie_manipulation/hardcode.yml b/examples/postman_echo/cookie_manipulation/hardcode.yml new file mode 100644 index 00000000..f81c5f7c --- /dev/null +++ b/examples/postman_echo/cookie_manipulation/hardcode.yml @@ -0,0 +1,34 @@ +config: + name: "set & delete cookies." + base_url: "https://postman-echo.com" + verify: False + export: ["cookie_foo1", "cookie_foo3"] + +teststeps: +- + name: set cookie foo1 & foo2 & foo3 + request: + method: GET + url: /cookies/set + params: + foo1: bar1 + foo2: bar2 + headers: + User-Agent: HttpRunner/${get_httprunner_version()} + extract: + cookie_foo1: $.cookies.foo1 + validate: + - eq: ["status_code", 200] + - eq: ["cookies.foo1", "bar1"] +- + name: delete cookie foo2 + request: + method: GET + url: /cookies/delete?foo2 + headers: + User-Agent: HttpRunner/${get_httprunner_version()} + validate: + - eq: ["status_code", 200] + - ne: ["$.cookies.foo1", "$foo1"] + - eq: ["$.cookies.foo1", "$cookie_foo1"] + - eq: ["$.cookies.foo3", "$cookie_foo3"] diff --git a/examples/postman_echo/cookie_manipulation/hardcode_test.py b/examples/postman_echo/cookie_manipulation/hardcode_test.py new file mode 100644 index 00000000..000d0404 --- /dev/null +++ b/examples/postman_echo/cookie_manipulation/hardcode_test.py @@ -0,0 +1,54 @@ +# NOTICE: Generated By HttpRunner. DO NOT EDIT! +# FROM: examples/postman_echo/cookie_manipulation/hardcode.yml +from httprunner import HttpRunner, TConfig, TStep + + +class TestCaseHardcode(HttpRunner): + config = TConfig( + **{ + "name": "set & delete cookies.", + "base_url": "https://postman-echo.com", + "verify": False, + "export": ["cookie_foo1", "cookie_foo3"], + "path": "examples/postman_echo/cookie_manipulation/hardcode_test.py", + } + ) + + teststeps = [ + TStep( + **{ + "name": "set cookie foo1 & foo2 & foo3", + "request": { + "method": "GET", + "url": "/cookies/set", + "params": {"foo1": "bar1", "foo2": "bar2"}, + "headers": {"User-Agent": "HttpRunner/${get_httprunner_version()}"}, + }, + "extract": {"cookie_foo1": "$.cookies.foo1"}, + "validate": [ + {"eq": ["status_code", 200]}, + {"eq": ["cookies.foo1", "bar1"]}, + ], + } + ), + TStep( + **{ + "name": "delete cookie foo2", + "request": { + "method": "GET", + "url": "/cookies/delete?foo2", + "headers": {"User-Agent": "HttpRunner/${get_httprunner_version()}"}, + }, + "validate": [ + {"eq": ["status_code", 200]}, + {"ne": ["$.cookies.foo1", "$foo1"]}, + {"eq": ["$.cookies.foo1", "$cookie_foo1"]}, + {"eq": ["$.cookies.foo3", "$cookie_foo3"]}, + ], + } + ), + ] + + +if __name__ == "__main__": + TestCaseHardcode().test_start() diff --git a/examples/postman_echo/cookie_manipulation/set_delete_cookies.py b/examples/postman_echo/cookie_manipulation/set_delete_cookies.py new file mode 100644 index 00000000..b2e3bf26 --- /dev/null +++ b/examples/postman_echo/cookie_manipulation/set_delete_cookies.py @@ -0,0 +1,62 @@ +import unittest +import requests + +from httprunner.runner import HttpRunner +from httprunner.schema import TConfig, TStep + + +class TestCaseSetDeleteCookies(unittest.TestCase): + config = TConfig( + **{ + "name": "set & delete cookies.", + "base_url": "https://postman-echo.com", + "variables": {"foo1": "bar1", "foo2": "bar2"}, + "verify": False, + "export": ["cookie_foo1", "cookie_foo3"], + } + ) + + teststeps = [ + TStep( + **{ + "name": "set cookie foo1 & foo2 & foo3", + "variables": {"foo3": "bar3"}, + "request": { + "method": "GET", + "url": "/cookies/set", + "params": {"foo1": "bar111", "foo2": "$foo2", "foo3": "$foo3"}, + "headers": {"User-Agent": "HttpRunner/${get_httprunner_version()}"}, + }, + "extract": { + "cookie_foo1": "$.cookies.foo1", + "cookie_foo3": "$.cookies.foo3", + }, + "validate": [ + {"eq": ["status_code", 200]}, + {"eq": ["$.cookies.foo3", "$foo3"]}, + ], + } + ), + TStep( + **{ + "name": "delete cookie foo2", + "request": { + "method": "GET", + "url": "/cookies/delete?foo2", + "headers": {"User-Agent": "HttpRunner/${get_httprunner_version()}"}, + }, + "validate": [ + {"eq": ["status_code", 200]}, + {"ne": ["$.cookies.foo1", "$foo1"]}, + {"eq": ["$.cookies.foo1", "$cookie_foo1"]}, + {"eq": ["$.cookies.foo3", "$cookie_foo3"]}, + ], + } + ), + ] + + def test_start(self): + s = requests.Session() + HttpRunner(self.config, self.teststeps, session=s).with_variables( + foo1="bar123", foo2="bar22" + ).run() diff --git a/examples/postman_echo/cookie_manipulation/set_delete_cookies_test.py b/examples/postman_echo/cookie_manipulation/set_delete_cookies_test.py new file mode 100644 index 00000000..540a2eb1 --- /dev/null +++ b/examples/postman_echo/cookie_manipulation/set_delete_cookies_test.py @@ -0,0 +1,59 @@ +# NOTICE: Generated By HttpRunner. DO NOT EDIT! +# FROM: examples/postman_echo/cookie_manipulation/set_delete_cookies.yml +from httprunner import HttpRunner, TConfig, TStep + + +class TestCaseSetDeleteCookies(HttpRunner): + config = TConfig( + **{ + "name": "set & delete cookies.", + "variables": {"foo1": "bar1", "foo2": "bar2"}, + "base_url": "https://postman-echo.com", + "verify": False, + "export": ["cookie_foo1", "cookie_foo3"], + "path": "examples/postman_echo/cookie_manipulation/set_delete_cookies_test.py", + } + ) + + teststeps = [ + TStep( + **{ + "name": "set cookie foo1 & foo2 & foo3", + "variables": {"foo3": "bar3"}, + "request": { + "method": "GET", + "url": "/cookies/set", + "params": {"foo1": "bar111", "foo2": "$foo2", "foo3": "$foo3"}, + "headers": {"User-Agent": "HttpRunner/${get_httprunner_version()}"}, + }, + "extract": { + "cookie_foo1": "$.cookies.foo1", + "cookie_foo3": "$.cookies.foo3", + }, + "validate": [ + {"eq": ["status_code", 200]}, + {"ne": ["$.cookies.foo3", "$foo3"]}, + ], + } + ), + TStep( + **{ + "name": "delete cookie foo2", + "request": { + "method": "GET", + "url": "/cookies/delete?foo2", + "headers": {"User-Agent": "HttpRunner/${get_httprunner_version()}"}, + }, + "validate": [ + {"eq": ["status_code", 200]}, + {"ne": ["$.cookies.foo1", "$foo1"]}, + {"eq": ["$.cookies.foo1", "$cookie_foo1"]}, + {"eq": ["$.cookies.foo3", "$cookie_foo3"]}, + ], + } + ), + ] + + +if __name__ == "__main__": + TestCaseSetDeleteCookies().test_start() diff --git a/examples/postman_echo/request_methods/demo_testsuite_yml/__init__.py b/examples/postman_echo/request_methods/demo_testsuite_yml/__init__.py index bed305d5..70cfba53 100644 --- a/examples/postman_echo/request_methods/demo_testsuite_yml/__init__.py +++ b/examples/postman_echo/request_methods/demo_testsuite_yml/__init__.py @@ -1 +1 @@ -# NOTICE: Generated By HttpRunner. DO NOT EDIT! \ No newline at end of file +# NOTICE: Generated By HttpRunner. DO NOT EDIT! diff --git a/examples/postman_echo/request_methods/demo_testsuite_yml/request_with_functions_test.py b/examples/postman_echo/request_methods/demo_testsuite_yml/request_with_functions_test.py index 0bc92c4d..01d3e075 100644 --- a/examples/postman_echo/request_methods/demo_testsuite_yml/request_with_functions_test.py +++ b/examples/postman_echo/request_methods/demo_testsuite_yml/request_with_functions_test.py @@ -1,87 +1,69 @@ # NOTICE: Generated By HttpRunner. DO NOT EDIT! # FROM: examples/postman_echo/request_methods/request_with_functions.yml -from httprunner import HttpRunner, TConfig, TStep +from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase class TestCaseRequestWithFunctions(HttpRunner): - config = TConfig( - **{ - "name": "request with functions", - "variables": {"foo1": "session_bar1", "var1": "testsuite_val1"}, - "base_url": "https://postman-echo.com", - "verify": False, - "path": "examples/postman_echo/request_methods/demo_testsuite_yml/request_with_functions_test.py", - } + config = ( + Config("request with functions") + .variables(**{"foo1": "session_bar1", "var1": "testsuite_val1"}) + .base_url("https://postman-echo.com") + .verify(False) ) teststeps = [ - TStep( - **{ - "name": "get with params", - "variables": { - "foo1": "bar1", - "foo2": "session_bar2", - "sum_v": "${sum_two(1, 2)}", - }, - "request": { - "method": "GET", - "url": "/get", - "params": {"foo1": "$foo1", "foo2": "$foo2", "sum_v": "$sum_v"}, - "headers": {"User-Agent": "HttpRunner/${get_httprunner_version()}"}, - }, - "extract": {"session_foo2": "body.args.foo2"}, - "validate": [ - {"eq": ["status_code", 200]}, - {"eq": ["body.args.foo1", "session_bar1"]}, - {"eq": ["body.args.sum_v", "3"]}, - {"eq": ["body.args.foo2", "session_bar2"]}, - ], - } + Step( + RunRequest("get with params") + .with_variables( + **{"foo1": "bar1", "foo2": "session_bar2", "sum_v": "${sum_two(1, 2)}"} + ) + .get("/get") + .with_params(**{"foo1": "$foo1", "foo2": "$foo2", "sum_v": "$sum_v"}) + .with_headers(**{"User-Agent": "HttpRunner/${get_httprunner_version()}"}) + .extract() + .with_jmespath("body.args.foo2", "session_foo2") + .validate() + .assert_equal("status_code", 200) + .assert_equal("body.args.foo1", "session_bar1") + .assert_equal("body.args.sum_v", "3") + .assert_equal("body.args.foo2", "session_bar2") ), - TStep( - **{ - "name": "post raw text", - "variables": {"foo1": "hello world", "foo3": "$session_foo2"}, - "request": { - "method": "POST", - "url": "/post", - "headers": { - "User-Agent": "HttpRunner/${get_httprunner_version()}", - "Content-Type": "text/plain", - }, - "data": "This is expected to be sent back as part of response body: $foo1-$foo3.", - }, - "validate": [ - {"eq": ["status_code", 200]}, - { - "eq": [ - "body.data", - "This is expected to be sent back as part of response body: session_bar1-session_bar2.", - ] - }, - ], - } + Step( + RunRequest("post raw text") + .with_variables(**{"foo1": "hello world", "foo3": "$session_foo2"}) + .post("/post") + .with_headers( + **{ + "User-Agent": "HttpRunner/${get_httprunner_version()}", + "Content-Type": "text/plain", + } + ) + .with_data( + "This is expected to be sent back as part of response body: $foo1-$foo3." + ) + .validate() + .assert_equal("status_code", 200) + .assert_equal( + "body.data", + "This is expected to be sent back as part of response body: session_bar1-session_bar2.", + ) ), - TStep( - **{ - "name": "post form data", - "variables": {"foo1": "bar1", "foo2": "bar2"}, - "request": { - "method": "POST", - "url": "/post", - "headers": { - "User-Agent": "HttpRunner/${get_httprunner_version()}", - "Content-Type": "application/x-www-form-urlencoded", - }, - "data": "foo1=$foo1&foo2=$foo2", - }, - "validate": [ - {"eq": ["status_code", 200]}, - {"eq": ["body.form.foo1", "session_bar1"]}, - {"eq": ["body.form.foo2", "bar2"]}, - ], - } + Step( + RunRequest("post form data") + .with_variables(**{"foo1": "bar1", "foo2": "bar2"}) + .post("/post") + .with_headers( + **{ + "User-Agent": "HttpRunner/${get_httprunner_version()}", + "Content-Type": "application/x-www-form-urlencoded", + } + ) + .with_data("foo1=$foo1&foo2=$foo2") + .validate() + .assert_equal("status_code", 200) + .assert_equal("body.form.foo1", "session_bar1") + .assert_equal("body.form.foo2", "bar2") ), ] diff --git a/examples/postman_echo/request_methods/demo_testsuite_yml/request_with_testcase_reference_test.py b/examples/postman_echo/request_methods/demo_testsuite_yml/request_with_testcase_reference_test.py index 187b5e23..dac68fef 100644 --- a/examples/postman_echo/request_methods/demo_testsuite_yml/request_with_testcase_reference_test.py +++ b/examples/postman_echo/request_methods/demo_testsuite_yml/request_with_testcase_reference_test.py @@ -6,7 +6,7 @@ import sys sys.path.insert(0, os.getcwd()) -from httprunner import HttpRunner, TConfig, TStep +from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase from examples.postman_echo.request_methods.request_with_functions_test import ( TestCaseRequestWithFunctions as RequestWithFunctions, @@ -14,23 +14,18 @@ from examples.postman_echo.request_methods.request_with_functions_test import ( class TestCaseRequestWithTestcaseReference(HttpRunner): - config = TConfig( - **{ - "name": "request with referenced testcase", - "variables": {"foo1": "session_bar1", "var2": "testsuite_val2"}, - "base_url": "https://postman-echo.com", - "verify": False, - "path": "examples/postman_echo/request_methods/demo_testsuite_yml/request_with_testcase_reference_test.py", - } + config = ( + Config("request with referenced testcase") + .variables(**{"foo1": "session_bar1", "var2": "testsuite_val2"}) + .base_url("https://postman-echo.com") + .verify(False) ) teststeps = [ - TStep( - **{ - "name": "request with functions", - "variables": {"foo1": "override_bar1"}, - "testcase": RequestWithFunctions, - } + Step( + RunTestCase("request with functions") + .with_variables(**{"foo1": "override_bar1"}) + .call(RequestWithFunctions) ), ] diff --git a/examples/postman_echo/request_methods/hardcode_test.py b/examples/postman_echo/request_methods/hardcode_test.py index f9931709..4fd3aa2c 100644 --- a/examples/postman_echo/request_methods/hardcode_test.py +++ b/examples/postman_echo/request_methods/hardcode_test.py @@ -1,77 +1,57 @@ # NOTICE: Generated By HttpRunner. DO NOT EDIT! # FROM: examples/postman_echo/request_methods/hardcode.yml -from httprunner import HttpRunner, TConfig, TStep +from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase class TestCaseHardcode(HttpRunner): - config = TConfig( - **{ - "name": "request methods testcase in hardcode", - "base_url": "https://postman-echo.com", - "verify": False, - "path": "examples/postman_echo/request_methods/hardcode_test.py", - "variables": {}, - } + config = ( + Config("request methods testcase in hardcode") + .base_url("https://postman-echo.com") + .verify(False) ) teststeps = [ - TStep( - **{ - "name": "get with params", - "request": { - "method": "GET", - "url": "/get", - "params": {"foo1": "bar1", "foo2": "bar2"}, - "headers": {"User-Agent": "HttpRunner/3.0"}, - }, - "validate": [{"eq": ["status_code", 200]}], - } + Step( + RunRequest("get with params") + .get("/get") + .with_params(**{"foo1": "bar1", "foo2": "bar2"}) + .with_headers(**{"User-Agent": "HttpRunner/3.0"}) + .validate() + .assert_equal("status_code", 200) ), - TStep( - **{ - "name": "post raw text", - "request": { - "method": "POST", - "url": "/post", - "headers": { - "User-Agent": "HttpRunner/3.0", - "Content-Type": "text/plain", - }, - "data": "This is expected to be sent back as part of response body.", - }, - "validate": [{"eq": ["status_code", 200]}], - } + Step( + RunRequest("post raw text") + .post("/post") + .with_headers( + **{"User-Agent": "HttpRunner/3.0", "Content-Type": "text/plain"} + ) + .with_data("This is expected to be sent back as part of response body.") + .validate() + .assert_equal("status_code", 200) ), - TStep( - **{ - "name": "post form data", - "request": { - "method": "POST", - "url": "/post", - "headers": { - "User-Agent": "HttpRunner/3.0", - "Content-Type": "application/x-www-form-urlencoded", - }, - "data": "foo1=bar1&foo2=bar2", - }, - "validate": [{"eq": ["status_code", 200]}], - } + Step( + RunRequest("post form data") + .post("/post") + .with_headers( + **{ + "User-Agent": "HttpRunner/3.0", + "Content-Type": "application/x-www-form-urlencoded", + } + ) + .with_data("foo1=bar1&foo2=bar2") + .validate() + .assert_equal("status_code", 200) ), - TStep( - **{ - "name": "put request", - "request": { - "method": "PUT", - "url": "/put", - "headers": { - "User-Agent": "HttpRunner/3.0", - "Content-Type": "text/plain", - }, - "data": "This is expected to be sent back as part of response body.", - }, - "validate": [{"eq": ["status_code", 200]}], - } + Step( + RunRequest("put request") + .put("/put") + .with_headers( + **{"User-Agent": "HttpRunner/3.0", "Content-Type": "text/plain"} + ) + .with_data("This is expected to be sent back as part of response body.") + .validate() + .assert_equal("status_code", 200) ), ] diff --git a/examples/postman_echo/request_methods/request_with_functions_test.py b/examples/postman_echo/request_methods/request_with_functions_test.py index e084d808..b8f333bb 100644 --- a/examples/postman_echo/request_methods/request_with_functions_test.py +++ b/examples/postman_echo/request_methods/request_with_functions_test.py @@ -1,67 +1,88 @@ # NOTICE: Generated By HttpRunner. DO NOT EDIT! # FROM: examples/postman_echo/request_methods/request_with_functions.yml -from httprunner import HttpRunner, Config, Step, Request +from httprunner import HttpRunner, TConfig, TStep class TestCaseRequestWithFunctions(HttpRunner): - config = ( - Config("request methods testcase with functions") - .variables(foo1="session_bar1") - .base_url("https://postman-echo.com") - .verify(False) + config = TConfig( + **{ + "name": "request methods testcase with functions", + "variables": {"foo1": "session_bar1"}, + "base_url": "https://postman-echo.com", + "verify": False, + "path": "examples/postman_echo/request_methods/request_with_functions_test.py", + } ) teststeps = [ - Step("get with params") - .with_variables(foo1="bar1", foo2="session_bar2", sum_v="${sum_two(1, 2)}") - .set_extractor("session_foo2", "body.args.foo2") - .run_request( - Request() - .get("/get") - .with_params(foo1="$foo1", foo2="$foo2", sum_v="$sum_v") - .with_headers(**{"User-Agent": "HttpRunner/${get_httprunner_version()}"}) - ) - .assert_equal("status_code", 200) - .assert_equal("body.args.foo1", "session_bar1") - .assert_equal("body.args.sum_v", "3") - .assert_equal("body.args.foo2", "session_bar2"), - Step("post raw text") - .with_variables(foo1="hello world", foo3="$session_foo2") - .run_request( - Request() - .post("/post") - .with_headers( - **{ - "User-Agent": "HttpRunner/${get_httprunner_version()}", - "Content-Type": "text/plain", - } - ) - .with_data( - "This is expected to be sent back as part of response body: $foo1-$foo3." - ) - ) - .assert_equal("status_code", 200) - .assert_equal( - "body.data", - "This is expected to be sent back as part of response body: session_bar1-session_bar2.", + TStep( + **{ + "name": "get with params", + "variables": { + "foo1": "bar1", + "foo2": "session_bar2", + "sum_v": "${sum_two(1, 2)}", + }, + "request": { + "method": "GET", + "url": "/get", + "params": {"foo1": "$foo1", "foo2": "$foo2", "sum_v": "$sum_v"}, + "headers": {"User-Agent": "HttpRunner/${get_httprunner_version()}"}, + }, + "extract": {"session_foo2": "body.args.foo2"}, + "validate": [ + {"eq": ["status_code", 200]}, + {"eq": ["body.args.foo1", "session_bar1"]}, + {"eq": ["body.args.sum_v", "3"]}, + {"eq": ["body.args.foo2", "session_bar2"]}, + ], + } + ), + TStep( + **{ + "name": "post raw text", + "variables": {"foo1": "hello world", "foo3": "$session_foo2"}, + "request": { + "method": "POST", + "url": "/post", + "headers": { + "User-Agent": "HttpRunner/${get_httprunner_version()}", + "Content-Type": "text/plain", + }, + "data": "This is expected to be sent back as part of response body: $foo1-$foo3.", + }, + "validate": [ + {"eq": ["status_code", 200]}, + { + "eq": [ + "body.data", + "This is expected to be sent back as part of response body: session_bar1-session_bar2.", + ] + }, + ], + } + ), + TStep( + **{ + "name": "post form data", + "variables": {"foo1": "bar1", "foo2": "bar2"}, + "request": { + "method": "POST", + "url": "/post", + "headers": { + "User-Agent": "HttpRunner/${get_httprunner_version()}", + "Content-Type": "application/x-www-form-urlencoded", + }, + "data": "foo1=$foo1&foo2=$foo2", + }, + "validate": [ + {"eq": ["status_code", 200]}, + {"eq": ["body.form.foo1", "session_bar1"]}, + {"eq": ["body.form.foo2", "bar2"]}, + ], + } ), - Step("post form data") - .with_variables(**{"foo1": "bar1", "foo2": "bar2"}) - .run_request( - Request() - .post("/post") - .with_headers( - **{ - "User-Agent": "HttpRunner/${get_httprunner_version()}", - "Content-Type": "application/x-www-form-urlencoded", - } - ) - .with_data("foo1=$foo1&foo2=$foo2") - ) - .assert_equal("status_code", 200) - .assert_equal("body.form.foo1", "session_bar1") - .assert_equal("body.form.foo2", "bar2"), ] diff --git a/examples/postman_echo/request_methods/request_with_testcase_reference_test.py b/examples/postman_echo/request_methods/request_with_testcase_reference_test.py index cb2fc35a..5d8ae4f2 100644 --- a/examples/postman_echo/request_methods/request_with_testcase_reference_test.py +++ b/examples/postman_echo/request_methods/request_with_testcase_reference_test.py @@ -6,7 +6,7 @@ import sys sys.path.insert(0, os.getcwd()) -from httprunner import HttpRunner, Config, Step +from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase from examples.postman_echo.request_methods.request_with_functions_test import ( TestCaseRequestWithFunctions as RequestWithFunctions, @@ -16,15 +16,17 @@ from examples.postman_echo.request_methods.request_with_functions_test import ( class TestCaseRequestWithTestcaseReference(HttpRunner): config = ( Config("request methods testcase: reference testcase") - .variables(foo1="session_bar1") + .variables(**{"foo1": "session_bar1"}) .base_url("https://postman-echo.com") .verify(False) ) teststeps = [ - Step("request with functions") - .with_variables(foo1="override_bar1") - .run_testcase(RequestWithFunctions), + Step( + RunTestCase("request with functions") + .with_variables(**{"foo1": "override_bar1"}) + .call(RequestWithFunctions) + ), ] diff --git a/examples/postman_echo/request_methods/request_with_variables_test.py b/examples/postman_echo/request_methods/request_with_variables_test.py index 6460eb90..d7fc5241 100644 --- a/examples/postman_echo/request_methods/request_with_variables_test.py +++ b/examples/postman_echo/request_methods/request_with_variables_test.py @@ -1,82 +1,63 @@ # NOTICE: Generated By HttpRunner. DO NOT EDIT! # FROM: examples/postman_echo/request_methods/request_with_variables.yml -from httprunner import HttpRunner, TConfig, TStep +from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase class TestCaseRequestWithVariables(HttpRunner): - config = TConfig( - **{ - "name": "request methods testcase with variables", - "variables": {"foo1": "session_bar1"}, - "base_url": "https://postman-echo.com", - "verify": False, - "path": "examples/postman_echo/request_methods/request_with_variables_test.py", - } + config = ( + Config("request methods testcase with variables") + .variables(**{"foo1": "session_bar1"}) + .base_url("https://postman-echo.com") + .verify(False) ) teststeps = [ - TStep( - **{ - "name": "get with params", - "variables": {"foo1": "bar1", "foo2": "session_bar2"}, - "request": { - "method": "GET", - "url": "/get", - "params": {"foo1": "$foo1", "foo2": "$foo2"}, - "headers": {"User-Agent": "HttpRunner/3.0"}, - }, - "extract": {"session_foo2": "body.args.foo2"}, - "validate": [ - {"eq": ["status_code", 200]}, - {"eq": ["body.args.foo1", "session_bar1"]}, - {"eq": ["body.args.foo2", "session_bar2"]}, - ], - } + Step( + RunRequest("get with params") + .with_variables(**{"foo1": "bar1", "foo2": "session_bar2"}) + .get("/get") + .with_params(**{"foo1": "$foo1", "foo2": "$foo2"}) + .with_headers(**{"User-Agent": "HttpRunner/3.0"}) + .extract() + .with_jmespath("body.args.foo2", "session_foo2") + .validate() + .assert_equal("status_code", 200) + .assert_equal("body.args.foo1", "session_bar1") + .assert_equal("body.args.foo2", "session_bar2") ), - TStep( - **{ - "name": "post raw text", - "variables": {"foo1": "hello world", "foo3": "$session_foo2"}, - "request": { - "method": "POST", - "url": "/post", - "headers": { - "User-Agent": "HttpRunner/3.0", - "Content-Type": "text/plain", - }, - "data": "This is expected to be sent back as part of response body: $foo1-$foo3.", - }, - "validate": [ - {"eq": ["status_code", 200]}, - { - "eq": [ - "body.data", - "This is expected to be sent back as part of response body: session_bar1-session_bar2.", - ] - }, - ], - } + Step( + RunRequest("post raw text") + .with_variables(**{"foo1": "hello world", "foo3": "$session_foo2"}) + .post("/post") + .with_headers( + **{"User-Agent": "HttpRunner/3.0", "Content-Type": "text/plain"} + ) + .with_data( + "This is expected to be sent back as part of response body: $foo1-$foo3." + ) + .validate() + .assert_equal("status_code", 200) + .assert_equal( + "body.data", + "This is expected to be sent back as part of response body: session_bar1-session_bar2.", + ) ), - TStep( - **{ - "name": "post form data", - "variables": {"foo1": "bar1", "foo2": "bar2"}, - "request": { - "method": "POST", - "url": "/post", - "headers": { - "User-Agent": "HttpRunner/3.0", - "Content-Type": "application/x-www-form-urlencoded", - }, - "data": "foo1=$foo1&foo2=$foo2", - }, - "validate": [ - {"eq": ["status_code", 200]}, - {"eq": ["body.form.foo1", "session_bar1"]}, - {"eq": ["body.form.foo2", "bar2"]}, - ], - } + Step( + RunRequest("post form data") + .with_variables(**{"foo1": "bar1", "foo2": "bar2"}) + .post("/post") + .with_headers( + **{ + "User-Agent": "HttpRunner/3.0", + "Content-Type": "application/x-www-form-urlencoded", + } + ) + .with_data("foo1=$foo1&foo2=$foo2") + .validate() + .assert_equal("status_code", 200) + .assert_equal("body.form.foo1", "session_bar1") + .assert_equal("body.form.foo2", "bar2") ), ] diff --git a/examples/postman_echo/request_methods/validate_with_functions_test.py b/examples/postman_echo/request_methods/validate_with_functions_test.py index 1346ec8e..6d39269d 100644 --- a/examples/postman_echo/request_methods/validate_with_functions_test.py +++ b/examples/postman_echo/request_methods/validate_with_functions_test.py @@ -1,41 +1,31 @@ # NOTICE: Generated By HttpRunner. DO NOT EDIT! # FROM: examples/postman_echo/request_methods/validate_with_functions.yml -from httprunner import HttpRunner, TConfig, TStep +from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase class TestCaseValidateWithFunctions(HttpRunner): - config = TConfig( - **{ - "name": "request methods testcase: validate with functions", - "variables": {"foo1": "session_bar1"}, - "base_url": "https://postman-echo.com", - "verify": False, - "path": "examples/postman_echo/request_methods/validate_with_functions_test.py", - } + config = ( + Config("request methods testcase: validate with functions") + .variables(**{"foo1": "session_bar1"}) + .base_url("https://postman-echo.com") + .verify(False) ) teststeps = [ - TStep( - **{ - "name": "get with params", - "variables": { - "foo1": "bar1", - "foo2": "session_bar2", - "sum_v": "${sum_two(1, 2)}", - }, - "request": { - "method": "GET", - "url": "/get", - "params": {"foo1": "$foo1", "foo2": "$foo2", "sum_v": "$sum_v"}, - "headers": {"User-Agent": "HttpRunner/${get_httprunner_version()}"}, - }, - "extract": {"session_foo2": "body.args.foo2"}, - "validate": [ - {"eq": ["status_code", 200]}, - {"eq": ["body.args.sum_v", "3"]}, - ], - } + Step( + RunRequest("get with params") + .with_variables( + **{"foo1": "bar1", "foo2": "session_bar2", "sum_v": "${sum_two(1, 2)}"} + ) + .get("/get") + .with_params(**{"foo1": "$foo1", "foo2": "$foo2", "sum_v": "$sum_v"}) + .with_headers(**{"User-Agent": "HttpRunner/${get_httprunner_version()}"}) + .extract() + .with_jmespath("body.args.foo2", "session_foo2") + .validate() + .assert_equal("status_code", 200) + .assert_equal("body.args.sum_v", "3") ), ] diff --git a/examples/postman_echo/request_methods/validate_with_variables_test.py b/examples/postman_echo/request_methods/validate_with_variables_test.py index 1cb75fa9..2ae99691 100644 --- a/examples/postman_echo/request_methods/validate_with_variables_test.py +++ b/examples/postman_echo/request_methods/validate_with_variables_test.py @@ -1,82 +1,63 @@ # NOTICE: Generated By HttpRunner. DO NOT EDIT! # FROM: examples/postman_echo/request_methods/validate_with_variables.yml -from httprunner import HttpRunner, TConfig, TStep +from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase class TestCaseValidateWithVariables(HttpRunner): - config = TConfig( - **{ - "name": "request methods testcase: validate with variables", - "variables": {"foo1": "session_bar1"}, - "base_url": "https://postman-echo.com", - "verify": False, - "path": "examples/postman_echo/request_methods/validate_with_variables_test.py", - } + config = ( + Config("request methods testcase: validate with variables") + .variables(**{"foo1": "session_bar1"}) + .base_url("https://postman-echo.com") + .verify(False) ) teststeps = [ - TStep( - **{ - "name": "get with params", - "variables": {"foo1": "bar1", "foo2": "session_bar2"}, - "request": { - "method": "GET", - "url": "/get", - "params": {"foo1": "$foo1", "foo2": "$foo2"}, - "headers": {"User-Agent": "HttpRunner/3.0"}, - }, - "extract": {"session_foo2": "body.args.foo2"}, - "validate": [ - {"eq": ["status_code", 200]}, - {"eq": ["body.args.foo1", "$foo1"]}, - {"eq": ["body.args.foo2", "$foo2"]}, - ], - } + Step( + RunRequest("get with params") + .with_variables(**{"foo1": "bar1", "foo2": "session_bar2"}) + .get("/get") + .with_params(**{"foo1": "$foo1", "foo2": "$foo2"}) + .with_headers(**{"User-Agent": "HttpRunner/3.0"}) + .extract() + .with_jmespath("body.args.foo2", "session_foo2") + .validate() + .assert_equal("status_code", 200) + .assert_equal("body.args.foo1", "$foo1") + .assert_equal("body.args.foo2", "$foo2") ), - TStep( - **{ - "name": "post raw text", - "variables": {"foo1": "hello world", "foo3": "$session_foo2"}, - "request": { - "method": "POST", - "url": "/post", - "headers": { - "User-Agent": "HttpRunner/3.0", - "Content-Type": "text/plain", - }, - "data": "This is expected to be sent back as part of response body: $foo1-$foo3.", - }, - "validate": [ - {"eq": ["status_code", 200]}, - { - "eq": [ - "body.data", - "This is expected to be sent back as part of response body: session_bar1-$foo3.", - ] - }, - ], - } + Step( + RunRequest("post raw text") + .with_variables(**{"foo1": "hello world", "foo3": "$session_foo2"}) + .post("/post") + .with_headers( + **{"User-Agent": "HttpRunner/3.0", "Content-Type": "text/plain"} + ) + .with_data( + "This is expected to be sent back as part of response body: $foo1-$foo3." + ) + .validate() + .assert_equal("status_code", 200) + .assert_equal( + "body.data", + "This is expected to be sent back as part of response body: session_bar1-$foo3.", + ) ), - TStep( - **{ - "name": "post form data", - "variables": {"foo1": "bar1", "foo2": "bar2"}, - "request": { - "method": "POST", - "url": "/post", - "headers": { - "User-Agent": "HttpRunner/3.0", - "Content-Type": "application/x-www-form-urlencoded", - }, - "data": "foo1=$foo1&foo2=$foo2", - }, - "validate": [ - {"eq": ["status_code", 200]}, - {"eq": ["body.form.foo1", "$foo1"]}, - {"eq": ["body.form.foo2", "$foo2"]}, - ], - } + Step( + RunRequest("post form data") + .with_variables(**{"foo1": "bar1", "foo2": "bar2"}) + .post("/post") + .with_headers( + **{ + "User-Agent": "HttpRunner/3.0", + "Content-Type": "application/x-www-form-urlencoded", + } + ) + .with_data("foo1=$foo1&foo2=$foo2") + .validate() + .assert_equal("status_code", 200) + .assert_equal("body.form.foo1", "$foo1") + .assert_equal("body.form.foo2", "$foo2") ), ] diff --git a/httprunner/__init__.py b/httprunner/__init__.py index 58134ded..71d2df85 100644 --- a/httprunner/__init__.py +++ b/httprunner/__init__.py @@ -3,7 +3,7 @@ __description__ = "One-stop solution for HTTP(S) testing." from httprunner.runner import HttpRunner from httprunner.schema import TConfig, TStep -from httprunner.testcase import Config, Step, Request +from httprunner.testcase import Config, Step, RunRequest, RunTestCase __all__ = [ "__version__", @@ -13,5 +13,6 @@ __all__ = [ "TStep", "Config", "Step", - "Request", + "RunRequest", + "RunTestCase", ] diff --git a/httprunner/cli.py b/httprunner/cli.py index 5297074d..762ae2a7 100644 --- a/httprunner/cli.py +++ b/httprunner/cli.py @@ -23,9 +23,14 @@ def main_run(extra_args): # keep compatibility with v2 extra_args = ensure_cli_args(extra_args) + chain_style = False tests_path_list = [] extra_args_new = [] for item in extra_args: + if item == "--chain-style": + chain_style = True + continue + if not os.path.exists(item): # item is not file/folder path extra_args_new.append(item) @@ -38,7 +43,7 @@ def main_run(extra_args): logger.error(f"No valid testcase path in cli arguments: {extra_args}") sys.exit(1) - testcase_path_list = main_make(tests_path_list) + testcase_path_list = main_make(tests_path_list, chain_style=chain_style) if not testcase_path_list: logger.error("No valid testcases found, exit 1.") sys.exit(1) @@ -110,7 +115,7 @@ def main(): elif sys.argv[1] == "har2case": main_har2case(args) elif sys.argv[1] == "make": - main_make(args.testcase_path) + main_make(args.testcase_path, chain_style=args.chain_style) def main_hrun_alias(): diff --git a/httprunner/make.py b/httprunner/make.py index 0d19aacd..a2ca8066 100644 --- a/httprunner/make.py +++ b/httprunner/make.py @@ -16,6 +16,7 @@ from httprunner.loader import ( load_project_meta, ) from httprunner.parser import parse_data +from httprunner.response import uniform_validator """ cache converted pytest files, avoid duplicate making """ @@ -50,6 +51,35 @@ if __name__ == "__main__": """ ) +__TEMPLATE_CHAIN_STYLE__ = jinja2.Template( + """# NOTICE: Generated By HttpRunner. DO NOT EDIT! +# FROM: {{ testcase_path }} +{% if imports_list %} +import os +import sys + +sys.path.insert(0, os.getcwd()) +{% endif %} +from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase +{% for import_str in imports_list %} +{{ import_str }} +{% endfor %} + +class {{ class_name }}(HttpRunner): + config = {{ config_chain_style }} + + teststeps = [ + {% for step_chain_style in teststeps_chain_style %} + {{ step_chain_style }}, + {% endfor %} + ] + +if __name__ == "__main__": + {{ class_name }}().test_start() + +""" +) + def __ensure_file_name(path: Text) -> Text: """ ensure file name not startswith digit @@ -96,7 +126,7 @@ def __ensure_testcase_module(path: Text) -> NoReturn: return with open(init_file, "w", encoding="utf-8") as f: - f.write("# NOTICE: Generated By HttpRunner. DO NOT EDIT!") + f.write("# NOTICE: Generated By HttpRunner. DO NOT EDIT!\n") def convert_testcase_path(testcase_path: Text) -> Tuple[Text, Text]: @@ -132,8 +162,105 @@ def __format_pytest_with_black(python_paths: List[Text]) -> NoReturn: logger.error(ex) +def make_config_chain_style(config: Dict) -> Text: + config_chain_style = f'Config("{config["name"]}")' + + if config["variables"]: + variables = config["variables"] + config_chain_style += f".variables(**{variables})" + + if "base_url" in config: + config_chain_style += f'.base_url("{config["base_url"]}")' + + if "verify" in config: + config_chain_style += f'.verify({config["verify"]})' + + return config_chain_style + + +def make_request_chain_style(request: Dict) -> Text: + method = request["method"].lower() + url = request["url"] + request_chain_style = f'.{method}("{url}")' + + if "params" in request: + params = request["params"] + request_chain_style += f".with_params(**{params})" + + if "headers" in request: + headers = request["headers"] + request_chain_style += f".with_headers(**{headers})" + + if "cookies" in request: + cookies = request["cookies"] + request_chain_style += f".with_cookies(**{cookies})" + + if "data" in request: + data = request["data"] + if isinstance(data, Text): + data = f'"{data}"' + request_chain_style += f".with_data({data})" + + if "timeout" in request: + timeout = request["timeout"] + request_chain_style += f".set_timeout({timeout})" + + if "verify" in request: + verify = request["verify"] + request_chain_style += f".set_verify({verify})" + + if "allow_redirects" in request: + allow_redirects = request["allow_redirects"] + request_chain_style += f".set_allow_redirects({allow_redirects})" + + return request_chain_style + + +def make_teststep_chain_style(teststep: Dict) -> Text: + if teststep.get("request"): + step_info = f'RunRequest("{teststep["name"]}")' + elif teststep.get("testcase"): + step_info = f'RunTestCase("{teststep["name"]}")' + else: + raise exceptions.TestCaseFormatError + + if "variables" in teststep: + variables = teststep["variables"] + step_info += f".with_variables(**{variables})" + + if teststep.get("request"): + step_info += make_request_chain_style(teststep["request"]) + elif teststep.get("testcase"): + testcase = teststep["testcase"].replace("CLS_LB(", "").replace(")CLS_RB", "") + call_ref_testcase = f".call({testcase})" + step_info += call_ref_testcase + + if "extract" in teststep: + step_info += ".extract()" + + for extract_name, extract_path in teststep["extract"].items(): + step_info += f'.with_jmespath("{extract_path}", "{extract_name}")' + + if "validate" in teststep: + step_info += ".validate()" + + for v in teststep["validate"]: + validator = uniform_validator(v) + assert_method = validator["assert"] + check = validator["check"] + expect = validator["expect"] + if isinstance(expect, Text): + expect = f'"{expect}"' + step_info += f'.assert_{assert_method}("{check}", {expect})' + + return f"Step({step_info})" + + def __make_testcase( - testcase: Dict, dir_path: Text = None, ref_flag: bool = False + testcase: Dict, + dir_path: Text = None, + ref_flag: bool = False, + chain_style: bool = False, ) -> NoReturn: """convert valid testcase dict to pytest file path""" # ensure compatibility with testcase format v2 @@ -195,12 +322,20 @@ def __make_testcase( data = { "testcase_path": __ensure_cwd_relative(testcase_path), "class_name": f"TestCase{testcase_cls_name}", - "config": config, - "teststeps": teststeps, "imports_list": imports_list, } - content = __TEMPLATE__.render(data) - content = content.replace("'CLS_LB(", "").replace(")CLS_RB'", "") + + if chain_style: + data["config_chain_style"] = make_config_chain_style(config) + data["teststeps_chain_style"] = [ + make_teststep_chain_style(step) for step in teststeps + ] + content = __TEMPLATE_CHAIN_STYLE__.render(data) + else: + data["config"] = config + data["teststeps"] = teststeps + content = __TEMPLATE__.render(data) + content = content.replace("'CLS_LB(", "").replace(")CLS_RB'", "") with open(testcase_python_path, "w", encoding="utf-8") as f: f.write(content) @@ -213,7 +348,7 @@ def __make_testcase( make_files_cache_set.add(__ensure_cwd_relative(testcase_python_path)) -def __make_testsuite(testsuite: Dict) -> NoReturn: +def __make_testsuite(testsuite: Dict, chain_style: bool = False) -> NoReturn: """convert valid testsuite dict to pytest folder with testcases""" # validate testsuite format load_testsuite(testsuite) @@ -258,10 +393,12 @@ def __make_testsuite(testsuite: Dict) -> NoReturn: testcase_dict["config"]["variables"].update(testsuite_variables) # make testcase - __make_testcase(testcase_dict, testsuite_dir) + __make_testcase(testcase_dict, testsuite_dir, chain_style=chain_style) -def __make(tests_path: Text, ref_flag: bool = False) -> NoReturn: +def __make( + tests_path: Text, ref_flag: bool = False, chain_style: bool = False +) -> NoReturn: """ make testcase(s) with testcase/testsuite/folder absolute path generated pytest file path will be cached in make_files_cache_set @@ -295,14 +432,16 @@ def __make(tests_path: Text, ref_flag: bool = False) -> NoReturn: # testcase if "teststeps" in test_content: try: - __make_testcase(test_content, ref_flag=ref_flag) + __make_testcase( + test_content, ref_flag=ref_flag, chain_style=chain_style + ) except exceptions.TestCaseFormatError: continue # testsuite elif "testcases" in test_content: try: - __make_testsuite(test_content) + __make_testsuite(test_content, chain_style=chain_style) except exceptions.TestSuiteFormatError: continue @@ -313,12 +452,12 @@ def __make(tests_path: Text, ref_flag: bool = False) -> NoReturn: ) -def main_make(tests_paths: List[Text]) -> List[Text]: +def main_make(tests_paths: List[Text], chain_style: bool = False) -> List[Text]: for tests_path in tests_paths: if not os.path.isabs(tests_path): tests_path = os.path.join(os.getcwd(), tests_path) - __make(tests_path) + __make(tests_path, chain_style=chain_style) testcase_path_list = list(make_files_cache_set) __format_pytest_with_black(testcase_path_list) @@ -334,5 +473,11 @@ def init_make_parser(subparsers): parser.add_argument( "testcase_path", nargs="*", help="Specify YAML/JSON testcase file/folder path" ) + parser.add_argument( + "--chain-style", + dest="chain_style", + action="store_true", + help="Convert pytest files in chain-style.", + ) return parser diff --git a/httprunner/runner.py b/httprunner/runner.py index 9a1c4774..b2c9060f 100644 --- a/httprunner/runner.py +++ b/httprunner/runner.py @@ -20,7 +20,7 @@ from httprunner.ext.uploader import prepare_upload_step from httprunner.loader import load_project_meta, load_testcase_file from httprunner.parser import build_url, parse_data, parse_variables_mapping from httprunner.response import ResponseObject -from httprunner.testcase import Config, StepValidation +from httprunner.testcase import Config, Step from httprunner.schema import ( TConfig, TStep, @@ -36,7 +36,7 @@ from httprunner.schema import ( class HttpRunner(object): config: Union[TConfig, Config] - teststeps: List[Union[TStep, StepValidation]] + teststeps: List[Union[TStep, Step]] success: bool = True # indicate testcase execution result __config: TConfig @@ -64,7 +64,7 @@ class HttpRunner(object): for step in self.teststeps: if isinstance(step, TStep): self.__teststeps.append(step) - elif isinstance(step, StepValidation): + elif isinstance(step, Step): self.__teststeps.append(step.perform()) else: raise exceptions.TestCaseFormatError(f"step type error: {step}") diff --git a/httprunner/testcase.py b/httprunner/testcase.py index a2af4c4a..1f772a2a 100644 --- a/httprunner/testcase.py +++ b/httprunner/testcase.py @@ -1,5 +1,5 @@ import inspect -from typing import Text, Any, Dict, Callable +from typing import Text, Any, Union, Callable from httprunner.schema import ( TConfig, @@ -50,154 +50,166 @@ class Config(object): ) -class RequestWithOptionalArgs(object): - def __init__(self, method: MethodEnum, url: Text): - self.__method = method - self.__url = url - self.__params = {} - self.__headers = {} - self.__cookies = {} - self.__data = "" - self.__timeout = 120 - self.__allow_redirects = True - self.__verify = False - - def with_params(self, **params) -> "RequestWithOptionalArgs": - self.__params.update(params) - return self - - def with_headers(self, **headers) -> "RequestWithOptionalArgs": - self.__headers.update(headers) - return self - - def with_cookies(self, **cookies) -> "RequestWithOptionalArgs": - self.__cookies.update(cookies) - return self - - def with_data(self, data) -> "RequestWithOptionalArgs": - self.__data = data - return self - - def set_timeout(self, timeout: float) -> "RequestWithOptionalArgs": - self.__timeout = timeout - return self - - def set_verify(self, verify: bool) -> "RequestWithOptionalArgs": - self.__verify = verify - return self - - def set_allow_redirects(self, allow_redirects: bool) -> "RequestWithOptionalArgs": - self.__allow_redirects = allow_redirects - return self - - def perform(self) -> TRequest: - """build TRequest object with configs""" - return TRequest( - method=self.__method, - url=self.__url, - params=self.__params, - headers=self.__headers, - data=self.__data, - timeout=self.__timeout, - verify=self.__verify, - allow_redirects=self.__allow_redirects, - ) - - -class Request(object): - def get(self, url: Text) -> RequestWithOptionalArgs: - return RequestWithOptionalArgs(MethodEnum.GET, url) - - def post(self, url: Text) -> RequestWithOptionalArgs: - return RequestWithOptionalArgs(MethodEnum.POST, url) - - def put(self, url: Text) -> RequestWithOptionalArgs: - return RequestWithOptionalArgs(MethodEnum.PUT, url) - - def head(self, url: Text) -> RequestWithOptionalArgs: - return RequestWithOptionalArgs(MethodEnum.HEAD, url) - - def delete(self, url: Text) -> RequestWithOptionalArgs: - return RequestWithOptionalArgs(MethodEnum.DELETE, url) - - def options(self, url: Text) -> RequestWithOptionalArgs: - return RequestWithOptionalArgs(MethodEnum.OPTIONS, url) - - def patch(self, url: Text) -> RequestWithOptionalArgs: - return RequestWithOptionalArgs(MethodEnum.PATCH, url) - - class StepValidation(object): - def __init__( - self, - name: Text, - variables: Dict, - extractors: Dict, - request: TRequest = None, - testcase: Callable = None, - ): - self.__name = name - self.__variables = variables - self.__extractors = extractors - self.__request: TRequest = request - self.__testcase: Callable = testcase - self.__validators = [] - - @property - def request(self) -> TRequest: - return self.__request - - @property - def testcase(self) -> TestCase: - return self.__testcase + def __init__(self, step: TStep): + self.__t_step = step def assert_equal(self, jmes_path: Text, expected_value: Any) -> "StepValidation": - self.__validators.append({"eq": [jmes_path, expected_value]}) + self.__t_step.validators.append({"eq": [jmes_path, expected_value]}) return self def assert_greater_than( self, jmes_path: Text, expected_value: Any ) -> "StepValidation": - self.__validators.append({"gt": [jmes_path, expected_value]}) + self.__t_step.validators.append({"gt": [jmes_path, expected_value]}) return self def assert_less_than( self, jmes_path: Text, expected_value: Any ) -> "StepValidation": - self.__validators.append({"lt": [jmes_path, expected_value]}) + self.__t_step.validators.append({"lt": [jmes_path, expected_value]}) return self def perform(self) -> TStep: - return TStep( - name=self.__name, - variables=self.__variables, - request=self.__request, - testcase=self.__testcase, - extract=self.__extractors, - validate=self.__validators, - ) + return self.__t_step + + +class StepExtraction(object): + def __init__(self, step: TStep): + self.__t_step = step + + def with_jmespath(self, jmes_path: Text, var_name: Text) -> "StepExtraction": + self.__t_step.extract[var_name] = jmes_path + return self + + # def with_regex(self): + # # TODO: extract response html with regex + # pass + # + # def with_jsonpath(self): + # # TODO: extract response json with jsonpath + # pass + + def validate(self) -> StepValidation: + return StepValidation(self.__t_step) + + def perform(self) -> TStep: + return self.__t_step + + +class RequestWithOptionalArgs(object): + def __init__(self, step: TStep): + self.__t_step = step + + def with_params(self, **params) -> "RequestWithOptionalArgs": + self.__t_step.request.params.update(params) + return self + + def with_headers(self, **headers) -> "RequestWithOptionalArgs": + self.__t_step.request.headers.update(headers) + return self + + def with_cookies(self, **cookies) -> "RequestWithOptionalArgs": + self.__t_step.request.cookies.update(cookies) + return self + + def with_data(self, data) -> "RequestWithOptionalArgs": + self.__t_step.request.data = data + return self + + def set_timeout(self, timeout: float) -> "RequestWithOptionalArgs": + self.__t_step.request.timeout = timeout + return self + + def set_verify(self, verify: bool) -> "RequestWithOptionalArgs": + self.__t_step.request.verify = verify + return self + + def set_allow_redirects(self, allow_redirects: bool) -> "RequestWithOptionalArgs": + self.__t_step.request.allow_redirects = allow_redirects + return self + + # def hooks(self): + # pass + + def extract(self) -> StepExtraction: + return StepExtraction(self.__t_step) + + def validate(self) -> StepValidation: + return StepValidation(self.__t_step) + + def perform(self) -> TStep: + return self.__t_step + + +class RunRequest(object): + def __init__(self, name: Text): + self.__t_step = TStep(name=name) + + def with_variables(self, **variables) -> "RunRequest": + self.__t_step.variables.update(variables) + return self + + def get(self, url: Text) -> RequestWithOptionalArgs: + self.__t_step.request = TRequest(method=MethodEnum.GET, url=url) + return RequestWithOptionalArgs(self.__t_step) + + def post(self, url: Text) -> RequestWithOptionalArgs: + self.__t_step.request = TRequest(method=MethodEnum.POST, url=url) + return RequestWithOptionalArgs(self.__t_step) + + def put(self, url: Text) -> RequestWithOptionalArgs: + self.__t_step.request = TRequest(method=MethodEnum.PUT, url=url) + return RequestWithOptionalArgs(self.__t_step) + + def head(self, url: Text) -> RequestWithOptionalArgs: + self.__t_step.request = TRequest(method=MethodEnum.HEAD, url=url) + return RequestWithOptionalArgs(self.__t_step) + + def delete(self, url: Text) -> RequestWithOptionalArgs: + self.__t_step.request = TRequest(method=MethodEnum.DELETE, url=url) + return RequestWithOptionalArgs(self.__t_step) + + def options(self, url: Text) -> RequestWithOptionalArgs: + self.__t_step.request = TRequest(method=MethodEnum.OPTIONS, url=url) + return RequestWithOptionalArgs(self.__t_step) + + def patch(self, url: Text) -> RequestWithOptionalArgs: + self.__t_step.request = TRequest(method=MethodEnum.PATCH, url=url) + return RequestWithOptionalArgs(self.__t_step) + + +class RunTestCase(object): + def __init__(self, name: Text): + self.__t_step = TStep(name=name) + + def with_variables(self, **variables) -> "RunTestCase": + self.__t_step.variables.update(variables) + return self + + def call(self, testcase: Callable): + self.__t_step.testcase = testcase + + def perform(self) -> TStep: + return self.__t_step class Step(object): - def __init__(self, name: Text): - self.__name = name - self.__variables = {} - self.__extractors = {} + def __init__( + self, + step: Union[ + StepValidation, StepExtraction, RequestWithOptionalArgs, RunTestCase + ], + ): + self.__t_step = step.perform() - def with_variables(self, **variables) -> "Step": - self.__variables.update(variables) - return self + @property + def request(self) -> TRequest: + return self.__t_step.request - def set_extractor(self, var_name: Text, jmes_path: Text) -> "Step": - self.__extractors[var_name] = jmes_path - return self + @property + def testcase(self) -> TestCase: + return self.__t_step.testcase - def run_request(self, req_obj: RequestWithOptionalArgs) -> "StepValidation": - return StepValidation( - self.__name, self.__variables, self.__extractors, request=req_obj.perform() - ) - - def run_testcase(self, testcase: Callable) -> "StepValidation": - return StepValidation( - self.__name, self.__variables, self.__extractors, testcase=testcase - ) + def perform(self) -> TStep: + return self.__t_step diff --git a/tests/make_test.py b/tests/make_test.py index 7884910e..703f5f10 100644 --- a/tests/make_test.py +++ b/tests/make_test.py @@ -1,6 +1,12 @@ import unittest -from httprunner.make import main_make, convert_testcase_path, make_files_cache_set +from httprunner.make import ( + main_make, + convert_testcase_path, + make_files_cache_set, + make_config_chain_style, + make_teststep_chain_style, +) class TestLoader(unittest.TestCase): @@ -95,3 +101,42 @@ from examples.postman_echo.request_methods.request_with_functions_test import ( "examples/postman_echo/request_methods/demo_testsuite_yml/request_with_testcase_reference_test.py", testcase_python_list, ) + + def test_make_config_chain_style(self): + config = { + "name": "request methods testcase: validate with functions", + "variables": {"foo1": "bar1", "foo2": 22}, + "base_url": "https://postman-echo.com", + "verify": False, + "path": "examples/postman_echo/request_methods/validate_with_functions_test.py", + } + self.assertEqual( + make_config_chain_style(config), + """Config("request methods testcase: validate with functions").variables(**{'foo1': 'bar1', 'foo2': 22}).base_url("https://postman-echo.com").verify(False)""", + ) + + def test_make_teststep_chain_style(self): + step = { + "name": "get with params", + "variables": {"foo1": "bar1", "foo2": 123, "sum_v": "${sum_two(1, 2)}",}, + "request": { + "method": "GET", + "url": "/get", + "params": {"foo1": "$foo1", "foo2": "$foo2", "sum_v": "$sum_v"}, + "headers": {"User-Agent": "HttpRunner/${get_httprunner_version()}"}, + }, + "testcase": "CLS_LB(TestCaseDemo)CLS_RB", + "extract": { + "session_foo1": "body.args.foo1", + "session_foo2": "body.args.foo2", + }, + "validate": [ + {"eq": ["status_code", 200]}, + {"eq": ["body.args.sum_v", "3"]}, + ], + } + teststep_chain_style = make_teststep_chain_style(step) + self.assertEqual( + teststep_chain_style, + """Step(RunRequest("get with params").with_variables(**{'foo1': 'bar1', 'foo2': 123, 'sum_v': '${sum_two(1, 2)}'}).get("/get").with_params(**{'foo1': '$foo1', 'foo2': '$foo2', 'sum_v': '$sum_v'}).with_headers(**{'User-Agent': 'HttpRunner/${get_httprunner_version()}'}).extract().with_jmespath("body.args.foo1", "session_foo1").with_jmespath("body.args.foo2", "session_foo2").validate().assert_equal("status_code", 200).assert_equal("body.args.sum_v", "3"))""", + )