From fceec5981577018a6d76c137e08daa09a6746590 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Mon, 8 Jun 2020 11:09:20 +0800 Subject: [PATCH 01/10] docs: update --- docs/installation.md | 14 +++++++------- mkdocs.yml | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/installation.md b/docs/installation.md index e9affc5b..4f4db7ad 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -6,20 +6,20 @@ `HttpRunner` is available on [`PyPI`][PyPI] and can be installed through `pip`. ```bash -$ pip install httprunner +$ pip3 install httprunner ``` If you want to keep up with the latest version, you can install with github repository url. ```bash -$ pip install git+https://github.com/httprunner/httprunner.git@master +$ pip3 install git+https://github.com/httprunner/httprunner.git@master ``` If you have installed `HttpRunner` before and want to upgrade to the latest version, you can use the `-U` option. ```bash -$ pip install -U httprunner -$ pip install -U git+https://github.com/httprunner/httprunner.git@master +$ pip3 install -U httprunner +$ pip3 install -U git+https://github.com/httprunner/httprunner.git@master ``` ## Check Installation @@ -27,15 +27,15 @@ $ pip install -U git+https://github.com/httprunner/httprunner.git@master When HttpRunner is installed, 4 commands will be added in your system. - `httprunner`: main command, used for all functions -- `hrun`: alias for `httprunner run`, used to run YAML/JSON testcases +- `hrun`: alias for `httprunner run`, used to run YAML/JSON/pytest testcases - `hmake`: alias for `httprunner make`, used to convert YAML/JSON testcases to pytest files - `har2case`: alias for `httprunner har2case`, used to convert HAR to YAML/JSON testcases To see `HttpRunner` version: ```text -$ httprunner -V -3.0.6 +$ httprunner -V # hrun -V +3.0.10 ``` To see available options, run: diff --git a/mkdocs.yml b/mkdocs.yml index 2899800e..8a9b4932 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -3,7 +3,7 @@ # $ pip install mkdocs-material # 5.2.2 # Project information -site_name: HttpRunner V3.x 中文使用文档 +site_name: HttpRunner V3.x Docs site_description: HttpRunner V3.x User Documentation site_author: 'debugtalk' @@ -28,7 +28,7 @@ theme: # Google Analytics google_analytics: - - 'UA-114587036-2' + - 'UA-114587036-3' - 'auto' # Extensions From 8ee713ac1232fb2aab086ea44954b70e1d2a4355 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Mon, 8 Jun 2020 11:45:21 +0800 Subject: [PATCH 02/10] change: log httprunner version --- docs/quickstart.md | 0 httprunner/cli.py | 1 + httprunner/runner.py | 2 +- 3 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 docs/quickstart.md diff --git a/docs/quickstart.md b/docs/quickstart.md new file mode 100644 index 00000000..e69de29b diff --git a/httprunner/cli.py b/httprunner/cli.py index c7f89910..70a4fca6 100644 --- a/httprunner/cli.py +++ b/httprunner/cli.py @@ -53,6 +53,7 @@ def main_run(extra_args) -> enum.IntEnum: extra_args_new.append("--tb=short") extra_args_new.extend(testcase_path_list) + logger.info(f"start to run tests with pytest. HttpRunner version: {__version__}") return pytest.main(extra_args_new) diff --git a/httprunner/runner.py b/httprunner/runner.py index 7dc18242..6eff19cd 100644 --- a/httprunner/runner.py +++ b/httprunner/runner.py @@ -2,7 +2,7 @@ import os import time import uuid from datetime import datetime -from typing import List, Dict, Text, NoReturn, Union +from typing import List, Dict, Text, NoReturn try: import allure From c19d1bb6641da78dddde70224613a54f62630a14 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Mon, 8 Jun 2020 12:51:32 +0800 Subject: [PATCH 03/10] fix: testsuite variables override testcase config variables --- httprunner/make.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/httprunner/make.py b/httprunner/make.py index 967e8d74..778fd783 100644 --- a/httprunner/make.py +++ b/httprunner/make.py @@ -407,11 +407,16 @@ def make_testsuite(testsuite: Dict) -> NoReturn: if "verify" in testsuite_config: testcase_dict["config"]["verify"] = testsuite_config["verify"] # override variables + # testsuite config variables > testsuite testcase variables testcase_variables = convert_variables( testcase.get("variables", {}), testcase_path ) testcase_variables.update(testsuite_variables) - testcase_dict["config"]["variables"] = testcase_variables + # testsuite testcase variables > testcase config variables + testcase_dict["config"]["variables"] = convert_variables( + testcase_dict["config"].get("variables", {}), testcase_path + ) + testcase_dict["config"]["variables"].update(testcase_variables) # make testcase testcase_pytest_path = make_testcase(testcase_dict, testsuite_dir) From 6220367394594d3c247078532c3c9d49ef98a1d2 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Mon, 8 Jun 2020 13:22:57 +0800 Subject: [PATCH 04/10] change: override variables (1) testcase: session variables > step variables > config variables (2) testsuite: testcase variables > config variables (3) testsuite testcase variables > testcase config variables --- examples/postman_echo/debugtalk.py | 8 +++-- .../request_methods/demo_testsuite.yml | 11 ++++-- .../request_with_functions_test.py | 36 +++++++++++-------- .../request_with_testcase_reference_test.py | 23 ++++++++---- .../request_methods/hardcode_test.py | 2 +- .../request_with_functions.yml | 35 +++++++++--------- .../request_with_functions_test.py | 36 +++++++++++-------- .../request_with_testcase_reference.yml | 15 ++++---- .../request_with_testcase_reference_test.py | 22 ++++++++---- .../request_with_variables.yml | 30 ++++++++-------- .../request_with_variables_test.py | 27 +++++++------- .../validate_with_functions.yml | 2 +- .../validate_with_functions_test.py | 2 +- .../validate_with_variables.yml | 2 +- .../validate_with_variables_test.py | 4 +-- httprunner/__init__.py | 2 +- httprunner/make.py | 9 ++--- httprunner/runner.py | 19 ++++++---- httprunner/utils.py | 22 ++++++++++++ pyproject.toml | 2 +- tests/utils_test.py | 14 +++++++- 21 files changed, 207 insertions(+), 116 deletions(-) diff --git a/examples/postman_echo/debugtalk.py b/examples/postman_echo/debugtalk.py index af8b22eb..a574ab2a 100644 --- a/examples/postman_echo/debugtalk.py +++ b/examples/postman_echo/debugtalk.py @@ -9,5 +9,9 @@ def sum_two(m, n): return m + n -def get_variables(): - return {"foo1": "session_bar1"} +def get_testcase_config_variables(): + return {"foo1": "testcase_config_bar1", "foo2": "testcase_config_bar2"} + + +def get_testsuite_config_variables(): + return {"foo1": "testsuite_config_bar1", "foo2": "testsuite_config_bar2"} diff --git a/examples/postman_echo/request_methods/demo_testsuite.yml b/examples/postman_echo/request_methods/demo_testsuite.yml index 946d37d7..1825efe3 100644 --- a/examples/postman_echo/request_methods/demo_testsuite.yml +++ b/examples/postman_echo/request_methods/demo_testsuite.yml @@ -1,15 +1,20 @@ config: name: "demo testsuite" - variables: ${get_variables()} + variables: ${get_testsuite_config_variables()} testcases: - name: request with functions testcase: request_methods/request_with_functions.yml variables: - var1: testsuite_val1 + foo1: testcase_ref_bar11 + expect_foo1: testcase_ref_bar11 + expect_foo2: testsuite_config_bar2 - name: request with referenced testcase testcase: request_methods/request_with_testcase_reference.yml variables: - var2: testsuite_val2 + foo1: testcase_ref_bar12 + expect_foo1: testcase_ref_bar12 + foo2: testcase_ref_bar22 + expect_foo2: testcase_ref_bar22 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 0944f1c5..a2555229 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,4 +1,4 @@ -# NOTE: Generated By HttpRunner v3.0.10 +# NOTE: Generated By HttpRunner v3.0.11 # FROM: examples/postman_echo/request_methods/request_with_functions.yml from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase @@ -7,32 +7,39 @@ from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase class TestCaseRequestWithFunctions(HttpRunner): config = ( Config("request with functions") - .variables(**{"var1": "testsuite_val1", "foo1": "session_bar1"}) + .variables( + **{ + "foo1": "testcase_ref_bar11", + "foo2": "testsuite_config_bar2", + "expect_foo1": "testcase_ref_bar11", + "expect_foo2": "testsuite_config_bar2", + } + ) .base_url("https://postman-echo.com") .verify(False) - .export(*["session_foo2"]) + .export(*["foo3"]) ) teststeps = [ Step( RunRequest("get with params") .with_variables( - **{"foo1": "bar1", "foo2": "session_bar2", "sum_v": "${sum_two(1, 2)}"} + **{"foo1": "bar11", "foo2": "bar21", "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") + .with_jmespath("body.args.foo2", "foo3") .validate() .assert_equal("status_code", 200) - .assert_equal("body.args.foo1", "session_bar1") + .assert_equal("body.args.foo1", "bar11") .assert_equal("body.args.sum_v", "3") - .assert_equal("body.args.foo2", "session_bar2") + .assert_equal("body.args.foo2", "bar21") ), Step( RunRequest("post raw text") - .with_variables(**{"foo1": "hello world", "foo3": "$session_foo2"}) + .with_variables(**{"foo1": "bar12", "foo3": "bar32"}) .post("/post") .with_headers( **{ @@ -41,18 +48,18 @@ class TestCaseRequestWithFunctions(HttpRunner): } ) .with_data( - "This is expected to be sent back as part of response body: $foo1-$foo3." + "This is expected to be sent back as part of response body: $foo1-$foo2-$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.", + "This is expected to be sent back as part of response body: bar12-$expect_foo2-bar21.", ) ), Step( RunRequest("post form data") - .with_variables(**{"foo1": "bar1", "foo2": "bar2"}) + .with_variables(**{"foo2": "bar23"}) .post("/post") .with_headers( **{ @@ -60,11 +67,12 @@ class TestCaseRequestWithFunctions(HttpRunner): "Content-Type": "application/x-www-form-urlencoded", } ) - .with_data("foo1=$foo1&foo2=$foo2") + .with_data("foo1=$foo1&foo2=$foo2&foo3=$foo3") .validate() .assert_equal("status_code", 200) - .assert_equal("body.form.foo1", "session_bar1") - .assert_equal("body.form.foo2", "bar2") + .assert_equal("body.form.foo1", "$expect_foo1") + .assert_equal("body.form.foo2", "bar23") + .assert_equal("body.form.foo3", "bar21") ), ] 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 23fc387a..dff4fe71 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 @@ -1,4 +1,4 @@ -# NOTE: Generated By HttpRunner v3.0.10 +# NOTE: Generated By HttpRunner v3.0.11 # FROM: examples/postman_echo/request_methods/request_with_testcase_reference.yml import os @@ -16,7 +16,14 @@ from examples.postman_echo.request_methods.request_with_functions_test import ( class TestCaseRequestWithTestcaseReference(HttpRunner): config = ( Config("request with referenced testcase") - .variables(**{"var2": "testsuite_val2", "foo1": "session_bar1"}) + .variables( + **{ + "foo1": "testcase_ref_bar12", + "expect_foo1": "testcase_ref_bar12", + "expect_foo2": "testcase_ref_bar22", + "foo2": "testcase_ref_bar22", + } + ) .base_url("https://postman-echo.com") .verify(False) ) @@ -24,9 +31,11 @@ class TestCaseRequestWithTestcaseReference(HttpRunner): teststeps = [ Step( RunTestCase("request with functions") - .with_variables(**{"foo1": "override_bar1"}) + .with_variables( + **{"foo1": "testcase_ref_bar1", "expect_foo1": "testcase_ref_bar1"} + ) .call(RequestWithFunctions) - .export(*["session_foo2"]) + .export(*["foo3"]) ), Step( RunRequest("post form data") @@ -38,11 +47,11 @@ class TestCaseRequestWithTestcaseReference(HttpRunner): "Content-Type": "application/x-www-form-urlencoded", } ) - .with_data("foo1=$foo1&foo2=$session_foo2") + .with_data("foo1=$foo1&foo2=$foo3") .validate() .assert_equal("status_code", 200) - .assert_equal("body.form.foo1", "session_bar1") - .assert_equal("body.form.foo2", "session_bar2") + .assert_equal("body.form.foo1", "bar1") + .assert_equal("body.form.foo2", "bar21") ), ] diff --git a/examples/postman_echo/request_methods/hardcode_test.py b/examples/postman_echo/request_methods/hardcode_test.py index cc1ddb3b..266e0b8a 100644 --- a/examples/postman_echo/request_methods/hardcode_test.py +++ b/examples/postman_echo/request_methods/hardcode_test.py @@ -1,4 +1,4 @@ -# NOTE: Generated By HttpRunner v3.0.10 +# NOTE: Generated By HttpRunner v3.0.11 # FROM: examples/postman_echo/request_methods/hardcode.yml from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase diff --git a/examples/postman_echo/request_methods/request_with_functions.yml b/examples/postman_echo/request_methods/request_with_functions.yml index 33952cab..d2675cd6 100644 --- a/examples/postman_echo/request_methods/request_with_functions.yml +++ b/examples/postman_echo/request_methods/request_with_functions.yml @@ -1,17 +1,20 @@ config: name: "request methods testcase with functions" variables: - foo1: session_bar1 + foo1: config_bar1 + foo2: config_bar2 + expect_foo1: config_bar1 + expect_foo2: config_bar2 base_url: "https://postman-echo.com" verify: False - export: ["session_foo2"] + export: ["foo3"] teststeps: - name: get with params variables: - foo1: bar1 - foo2: session_bar2 + foo1: bar11 + foo2: bar21 sum_v: "${sum_two(1, 2)}" request: method: GET @@ -23,40 +26,40 @@ teststeps: headers: User-Agent: HttpRunner/${get_httprunner_version()} extract: - session_foo2: "body.args.foo2" + foo3: "body.args.foo2" validate: - eq: ["status_code", 200] - - eq: ["body.args.foo1", "session_bar1"] + - eq: ["body.args.foo1", "bar11"] - eq: ["body.args.sum_v", "3"] - - eq: ["body.args.foo2", "session_bar2"] + - eq: ["body.args.foo2", "bar21"] - name: post raw text variables: - foo1: "hello world" - foo3: "$session_foo2" + foo1: "bar12" + foo3: "bar32" 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." + data: "This is expected to be sent back as part of response body: $foo1-$foo2-$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."] + - eq: ["body.data", "This is expected to be sent back as part of response body: bar12-$expect_foo2-bar21."] - name: post form data variables: - foo1: bar1 - foo2: bar2 + foo2: bar23 request: method: POST url: /post headers: User-Agent: HttpRunner/${get_httprunner_version()} Content-Type: "application/x-www-form-urlencoded" - data: "foo1=$foo1&foo2=$foo2" + data: "foo1=$foo1&foo2=$foo2&foo3=$foo3" validate: - eq: ["status_code", 200] - - eq: ["body.form.foo1", "session_bar1"] - - eq: ["body.form.foo2", "bar2"] + - eq: ["body.form.foo1", "$expect_foo1"] + - eq: ["body.form.foo2", "bar23"] + - eq: ["body.form.foo3", "bar21"] 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 5ee630e0..5656a518 100644 --- a/examples/postman_echo/request_methods/request_with_functions_test.py +++ b/examples/postman_echo/request_methods/request_with_functions_test.py @@ -1,4 +1,4 @@ -# NOTE: Generated By HttpRunner v3.0.10 +# NOTE: Generated By HttpRunner v3.0.11 # FROM: examples/postman_echo/request_methods/request_with_functions.yml from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase @@ -7,32 +7,39 @@ from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase class TestCaseRequestWithFunctions(HttpRunner): config = ( Config("request methods testcase with functions") - .variables(**{"foo1": "session_bar1"}) + .variables( + **{ + "foo1": "config_bar1", + "foo2": "config_bar2", + "expect_foo1": "config_bar1", + "expect_foo2": "config_bar2", + } + ) .base_url("https://postman-echo.com") .verify(False) - .export(*["session_foo2"]) + .export(*["foo3"]) ) teststeps = [ Step( RunRequest("get with params") .with_variables( - **{"foo1": "bar1", "foo2": "session_bar2", "sum_v": "${sum_two(1, 2)}"} + **{"foo1": "bar11", "foo2": "bar21", "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") + .with_jmespath("body.args.foo2", "foo3") .validate() .assert_equal("status_code", 200) - .assert_equal("body.args.foo1", "session_bar1") + .assert_equal("body.args.foo1", "bar11") .assert_equal("body.args.sum_v", "3") - .assert_equal("body.args.foo2", "session_bar2") + .assert_equal("body.args.foo2", "bar21") ), Step( RunRequest("post raw text") - .with_variables(**{"foo1": "hello world", "foo3": "$session_foo2"}) + .with_variables(**{"foo1": "bar12", "foo3": "bar32"}) .post("/post") .with_headers( **{ @@ -41,18 +48,18 @@ class TestCaseRequestWithFunctions(HttpRunner): } ) .with_data( - "This is expected to be sent back as part of response body: $foo1-$foo3." + "This is expected to be sent back as part of response body: $foo1-$foo2-$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.", + "This is expected to be sent back as part of response body: bar12-$expect_foo2-bar21.", ) ), Step( RunRequest("post form data") - .with_variables(**{"foo1": "bar1", "foo2": "bar2"}) + .with_variables(**{"foo2": "bar23"}) .post("/post") .with_headers( **{ @@ -60,11 +67,12 @@ class TestCaseRequestWithFunctions(HttpRunner): "Content-Type": "application/x-www-form-urlencoded", } ) - .with_data("foo1=$foo1&foo2=$foo2") + .with_data("foo1=$foo1&foo2=$foo2&foo3=$foo3") .validate() .assert_equal("status_code", 200) - .assert_equal("body.form.foo1", "session_bar1") - .assert_equal("body.form.foo2", "bar2") + .assert_equal("body.form.foo1", "$expect_foo1") + .assert_equal("body.form.foo2", "bar23") + .assert_equal("body.form.foo3", "bar21") ), ] diff --git a/examples/postman_echo/request_methods/request_with_testcase_reference.yml b/examples/postman_echo/request_methods/request_with_testcase_reference.yml index 37871f2c..7cd6630e 100644 --- a/examples/postman_echo/request_methods/request_with_testcase_reference.yml +++ b/examples/postman_echo/request_methods/request_with_testcase_reference.yml @@ -1,7 +1,9 @@ config: name: "request methods testcase: reference testcase" variables: - foo1: session_bar1 + foo1: testsuite_config_bar1 + expect_foo1: testsuite_config_bar1 + expect_foo2: config_bar2 base_url: "https://postman-echo.com" verify: False @@ -9,10 +11,11 @@ teststeps: - name: request with functions variables: - foo1: override_bar1 + foo1: testcase_ref_bar1 + expect_foo1: testcase_ref_bar1 testcase: request_methods/request_with_functions.yml export: - - session_foo2 + - foo3 - name: post form data variables: @@ -23,8 +26,8 @@ teststeps: headers: User-Agent: HttpRunner/${get_httprunner_version()} Content-Type: "application/x-www-form-urlencoded" - data: "foo1=$foo1&foo2=$session_foo2" + data: "foo1=$foo1&foo2=$foo3" validate: - eq: ["status_code", 200] - - eq: ["body.form.foo1", "session_bar1"] - - eq: ["body.form.foo2", "session_bar2"] + - eq: ["body.form.foo1", "bar1"] + - eq: ["body.form.foo2", "bar21"] 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 27b7fcb0..2ebd81f7 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 @@ -1,4 +1,4 @@ -# NOTE: Generated By HttpRunner v3.0.10 +# NOTE: Generated By HttpRunner v3.0.11 # FROM: examples/postman_echo/request_methods/request_with_testcase_reference.yml import os @@ -16,7 +16,13 @@ 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": "testsuite_config_bar1", + "expect_foo1": "testsuite_config_bar1", + "expect_foo2": "config_bar2", + } + ) .base_url("https://postman-echo.com") .verify(False) ) @@ -24,9 +30,11 @@ class TestCaseRequestWithTestcaseReference(HttpRunner): teststeps = [ Step( RunTestCase("request with functions") - .with_variables(**{"foo1": "override_bar1"}) + .with_variables( + **{"foo1": "testcase_ref_bar1", "expect_foo1": "testcase_ref_bar1"} + ) .call(RequestWithFunctions) - .export(*["session_foo2"]) + .export(*["foo3"]) ), Step( RunRequest("post form data") @@ -38,11 +46,11 @@ class TestCaseRequestWithTestcaseReference(HttpRunner): "Content-Type": "application/x-www-form-urlencoded", } ) - .with_data("foo1=$foo1&foo2=$session_foo2") + .with_data("foo1=$foo1&foo2=$foo3") .validate() .assert_equal("status_code", 200) - .assert_equal("body.form.foo1", "session_bar1") - .assert_equal("body.form.foo2", "session_bar2") + .assert_equal("body.form.foo1", "bar1") + .assert_equal("body.form.foo2", "bar21") ), ] diff --git a/examples/postman_echo/request_methods/request_with_variables.yml b/examples/postman_echo/request_methods/request_with_variables.yml index b5d19cc2..7f9dec55 100644 --- a/examples/postman_echo/request_methods/request_with_variables.yml +++ b/examples/postman_echo/request_methods/request_with_variables.yml @@ -1,6 +1,6 @@ config: name: "request methods testcase with variables" - variables: ${get_variables()} + variables: ${get_testcase_config_variables()} base_url: "https://postman-echo.com" verify: False @@ -8,8 +8,8 @@ teststeps: - name: get with params variables: - foo1: bar1 - foo2: session_bar2 + foo1: bar11 + foo2: bar21 request: method: GET url: /get @@ -19,39 +19,39 @@ teststeps: headers: User-Agent: HttpRunner/3.0 extract: - session_foo2: "body.args.foo2" + foo3: "body.args.foo2" validate: - eq: ["status_code", 200] - - eq: ["body.args.foo1", "session_bar1"] - - eq: ["body.args.foo2", "session_bar2"] + - eq: ["body.args.foo1", "bar11"] + - eq: ["body.args.foo2", "bar21"] - name: post raw text variables: - foo1: "hello world" - foo3: "$session_foo2" + foo1: "bar12" + foo3: "bar32" 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." + data: "This is expected to be sent back as part of response body: $foo1-$foo2-$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."] + - eq: ["body.data", "This is expected to be sent back as part of response body: bar12-testcase_config_bar2-bar21."] - name: post form data variables: - foo1: bar1 - foo2: bar2 + foo2: bar23 request: method: POST url: /post headers: User-Agent: HttpRunner/3.0 Content-Type: "application/x-www-form-urlencoded" - data: "foo1=$foo1&foo2=$foo2" + data: "foo1=$foo1&foo2=$foo2&foo3=$foo3" validate: - eq: ["status_code", 200] - - eq: ["body.form.foo1", "session_bar1"] - - eq: ["body.form.foo2", "bar2"] + - eq: ["body.form.foo1", "testcase_config_bar1"] + - eq: ["body.form.foo2", "bar23"] + - eq: ["body.form.foo3", "bar21"] 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 5f8c0484..a48c10bd 100644 --- a/examples/postman_echo/request_methods/request_with_variables_test.py +++ b/examples/postman_echo/request_methods/request_with_variables_test.py @@ -1,4 +1,4 @@ -# NOTE: Generated By HttpRunner v3.0.10 +# NOTE: Generated By HttpRunner v3.0.11 # FROM: examples/postman_echo/request_methods/request_with_variables.yml from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase @@ -7,7 +7,7 @@ from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase class TestCaseRequestWithVariables(HttpRunner): config = ( Config("request methods testcase with variables") - .variables(**{"foo1": "session_bar1"}) + .variables(**{"foo1": "testcase_config_bar1", "foo2": "testcase_config_bar2"}) .base_url("https://postman-echo.com") .verify(False) ) @@ -15,37 +15,37 @@ class TestCaseRequestWithVariables(HttpRunner): teststeps = [ Step( RunRequest("get with params") - .with_variables(**{"foo1": "bar1", "foo2": "session_bar2"}) + .with_variables(**{"foo1": "bar11", "foo2": "bar21"}) .get("/get") .with_params(**{"foo1": "$foo1", "foo2": "$foo2"}) .with_headers(**{"User-Agent": "HttpRunner/3.0"}) .extract() - .with_jmespath("body.args.foo2", "session_foo2") + .with_jmespath("body.args.foo2", "foo3") .validate() .assert_equal("status_code", 200) - .assert_equal("body.args.foo1", "session_bar1") - .assert_equal("body.args.foo2", "session_bar2") + .assert_equal("body.args.foo1", "bar11") + .assert_equal("body.args.foo2", "bar21") ), Step( RunRequest("post raw text") - .with_variables(**{"foo1": "hello world", "foo3": "$session_foo2"}) + .with_variables(**{"foo1": "bar12", "foo3": "bar32"}) .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." + "This is expected to be sent back as part of response body: $foo1-$foo2-$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.", + "This is expected to be sent back as part of response body: bar12-testcase_config_bar2-bar21.", ) ), Step( RunRequest("post form data") - .with_variables(**{"foo1": "bar1", "foo2": "bar2"}) + .with_variables(**{"foo2": "bar23"}) .post("/post") .with_headers( **{ @@ -53,11 +53,12 @@ class TestCaseRequestWithVariables(HttpRunner): "Content-Type": "application/x-www-form-urlencoded", } ) - .with_data("foo1=$foo1&foo2=$foo2") + .with_data("foo1=$foo1&foo2=$foo2&foo3=$foo3") .validate() .assert_equal("status_code", 200) - .assert_equal("body.form.foo1", "session_bar1") - .assert_equal("body.form.foo2", "bar2") + .assert_equal("body.form.foo1", "testcase_config_bar1") + .assert_equal("body.form.foo2", "bar23") + .assert_equal("body.form.foo3", "bar21") ), ] diff --git a/examples/postman_echo/request_methods/validate_with_functions.yml b/examples/postman_echo/request_methods/validate_with_functions.yml index 4d8d8a83..608f0612 100644 --- a/examples/postman_echo/request_methods/validate_with_functions.yml +++ b/examples/postman_echo/request_methods/validate_with_functions.yml @@ -26,4 +26,4 @@ teststeps: validate: - eq: ["status_code", 200] - eq: ["body.args.sum_v", "3"] -# - less_than: ["body.args.sum_v", "${sum_two(2, 2)}"] TODO +# - less_than: ["body.args.sum_v", "${sum_two(2, 2)}"] FIXME: TypeError: '<' not supported between instances of 'str' and 'int' 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 185896af..09da4892 100644 --- a/examples/postman_echo/request_methods/validate_with_functions_test.py +++ b/examples/postman_echo/request_methods/validate_with_functions_test.py @@ -1,4 +1,4 @@ -# NOTE: Generated By HttpRunner v3.0.10 +# NOTE: Generated By HttpRunner v3.0.11 # FROM: examples/postman_echo/request_methods/validate_with_functions.yml from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase diff --git a/examples/postman_echo/request_methods/validate_with_variables.yml b/examples/postman_echo/request_methods/validate_with_variables.yml index 7f77219f..3044af99 100644 --- a/examples/postman_echo/request_methods/validate_with_variables.yml +++ b/examples/postman_echo/request_methods/validate_with_variables.yml @@ -39,7 +39,7 @@ teststeps: 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."] + - eq: ["body.data", "This is expected to be sent back as part of response body: hello world-$foo3."] - name: post form data variables: 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 05daa1ae..0d1c101a 100644 --- a/examples/postman_echo/request_methods/validate_with_variables_test.py +++ b/examples/postman_echo/request_methods/validate_with_variables_test.py @@ -1,4 +1,4 @@ -# NOTE: Generated By HttpRunner v3.0.10 +# NOTE: Generated By HttpRunner v3.0.11 # FROM: examples/postman_echo/request_methods/validate_with_variables.yml from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase @@ -40,7 +40,7 @@ class TestCaseValidateWithVariables(HttpRunner): .assert_equal("status_code", 200) .assert_equal( "body.data", - "This is expected to be sent back as part of response body: session_bar1-$foo3.", + "This is expected to be sent back as part of response body: hello world-$foo3.", ) ), Step( diff --git a/httprunner/__init__.py b/httprunner/__init__.py index 3c7e46bb..61b95cf3 100644 --- a/httprunner/__init__.py +++ b/httprunner/__init__.py @@ -1,4 +1,4 @@ -__version__ = "3.0.10" +__version__ = "3.0.11" __description__ = "One-stop solution for HTTP(S) testing." from httprunner.runner import HttpRunner diff --git a/httprunner/make.py b/httprunner/make.py index 778fd783..b4b24ae1 100644 --- a/httprunner/make.py +++ b/httprunner/make.py @@ -21,9 +21,8 @@ from httprunner.loader import ( load_testsuite, load_project_meta, ) -from httprunner.parser import parse_data from httprunner.response import uniform_validator -from httprunner.utils import ensure_file_path_valid +from httprunner.utils import ensure_file_path_valid, override_config_variables """ cache converted pytest files, avoid duplicate making """ @@ -407,11 +406,13 @@ def make_testsuite(testsuite: Dict) -> NoReturn: if "verify" in testsuite_config: testcase_dict["config"]["verify"] = testsuite_config["verify"] # override variables - # testsuite config variables > testsuite testcase variables + # testsuite testcase variables > testsuite config variables testcase_variables = convert_variables( testcase.get("variables", {}), testcase_path ) - testcase_variables.update(testsuite_variables) + testcase_variables = override_config_variables( + testcase_variables, testsuite_variables + ) # testsuite testcase variables > testcase config variables testcase_dict["config"]["variables"] = convert_variables( testcase_dict["config"].get("variables", {}), testcase_path diff --git a/httprunner/runner.py b/httprunner/runner.py index 6eff19cd..d71debd3 100644 --- a/httprunner/runner.py +++ b/httprunner/runner.py @@ -21,6 +21,7 @@ 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, Step +from httprunner.utils import override_config_variables from httprunner.models import ( TConfig, TStep, @@ -324,20 +325,26 @@ class HttpRunner(object): # run teststeps for step in self.__teststeps: - # update with config variables - step.variables.update(self.__config.variables) - # update with session variables extracted from pre step + # override variables + # session variables (extracted from pre step) > step variables step.variables.update(self.__session_variables) + # step variables > testcase config variables + step.variables = override_config_variables( + step.variables, self.__config.variables + ) + # parse variables step.variables = parse_variables_mapping( step.variables, self.__project_meta.functions ) + # run step if USE_ALLURE: with allure.step(f"step: {step.name}"): extract_mapping = self.__run_step(step) else: extract_mapping = self.__run_step(step) + # save extracted variables to session variables self.__session_variables.update(extract_mapping) @@ -413,10 +420,10 @@ class HttpRunner(object): log_handler = logger.add(self.__log_path, level="DEBUG") # parse config name - variables = self.__config.variables - variables.update(self.__session_variables) + config_variables = self.__config.variables + config_variables.update(self.__session_variables) self.__config.name = parse_data( - self.__config.name, variables, self.__project_meta.functions + self.__config.name, config_variables, self.__project_meta.functions ) if USE_ALLURE: diff --git a/httprunner/utils.py b/httprunner/utils.py index 117dfbbd..7ba3b147 100644 --- a/httprunner/utils.py +++ b/httprunner/utils.py @@ -1,4 +1,5 @@ import collections +import copy import json import os.path import platform @@ -11,6 +12,7 @@ from loguru import logger from httprunner import __version__ from httprunner import exceptions +from httprunner.models import VariablesMapping def init_sentry_sdk(): @@ -223,3 +225,23 @@ class ExtendJSONEncoder(json.JSONEncoder): return super(ExtendJSONEncoder, self).default(obj) except (UnicodeDecodeError, TypeError): return repr(obj) + + +def override_config_variables( + step_variables: VariablesMapping, config_variables: VariablesMapping +) -> VariablesMapping: + """ override variables: + testcase step variables > testcase config variables + testsuite testcase variables > testsuite config variables + """ + step_new_variables = {} + for key, value in step_variables.items(): + if f"${key}" == value: + # e.g. {"base_url": "$base_url"} + continue + + step_new_variables[key] = value + + variables = copy.deepcopy(config_variables) + variables.update(step_new_variables) + return variables diff --git a/pyproject.toml b/pyproject.toml index 548b4bcf..9eb460f6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "httprunner" -version = "3.0.10" +version = "3.0.11" description = "One-stop solution for HTTP(S) testing." license = "Apache-2.0" readme = "README.md" diff --git a/tests/utils_test.py b/tests/utils_test.py index 9903c1d9..b47961c7 100644 --- a/tests/utils_test.py +++ b/tests/utils_test.py @@ -4,7 +4,11 @@ import os import unittest from httprunner import loader, utils -from httprunner.utils import ensure_file_path_valid, ExtendJSONEncoder +from httprunner.utils import ( + ensure_file_path_valid, + ExtendJSONEncoder, + override_config_variables, +) class TestUtils(unittest.TestCase): @@ -129,3 +133,11 @@ class TestUtils(unittest.TestCase): json.dumps(data) json.dumps(data, cls=ExtendJSONEncoder) + + def test_override_config_variables(self): + step_variables = {"base_url": "$base_url", "foo1": "bar1"} + config_variables = {"base_url": "https://httpbin.org", "foo1": "bar111"} + self.assertEqual( + override_config_variables(step_variables, config_variables), + {"base_url": "https://httpbin.org", "foo1": "bar111"}, + ) From 9a3962b995a0a68a951139620b3090ea235d8303 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Mon, 8 Jun 2020 16:22:05 +0800 Subject: [PATCH 05/10] fix: incorrect summary success when testcase failed --- docs/CHANGELOG.md | 6 ++++++ httprunner/runner.py | 6 +++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 46d791c1..a3137538 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -1,5 +1,11 @@ # Release History +## 3.0.11 (2020-06-08) + +**Fixed** + +- fix: incorrect summary success when testcase failed + ## 3.0.10 (2020-06-07) **Added** diff --git a/httprunner/runner.py b/httprunner/runner.py index d71debd3..f2c06c25 100644 --- a/httprunner/runner.py +++ b/httprunner/runner.py @@ -40,7 +40,7 @@ class HttpRunner(object): config: Config teststeps: List[Step] - success: bool = True # indicate testcase execution result + success: bool = False # indicate testcase execution result __config: TConfig __teststeps: List[TStep] __project_meta: ProjectMeta = None @@ -217,7 +217,7 @@ class HttpRunner(object): finally: # save request & response meta data self.__session.data.validators = resp_obj.validation_results - self.success &= self.__session.data.success + self.success = self.__session.data.success # save step data step_data.success = self.__session.data.success step_data.data = self.__session.data @@ -266,7 +266,7 @@ class HttpRunner(object): step_data.data = case_result.get_step_datas() # list of step data step_data.export_vars = case_result.get_export_variables() step_data.success = case_result.success - self.success &= case_result.success + self.success = case_result.success if step_data.export_vars: logger.info(f"export variables: {step_data.export_vars}") From 97557a9b6f33d276041262d7852c27e634e4df5f Mon Sep 17 00:00:00 2001 From: debugtalk Date: Mon, 8 Jun 2020 16:26:56 +0800 Subject: [PATCH 06/10] docs: update changelog --- docs/CHANGELOG.md | 7 ++++ examples/httpbin/basic_test.py | 2 +- examples/httpbin/conftest.py | 64 +++++++++++++++++++++++++++++ examples/httpbin/hooks_test.py | 2 +- examples/httpbin/load_image_test.py | 2 +- examples/httpbin/upload_test.py | 2 +- examples/httpbin/validate_test.py | 2 +- httprunner/make.py | 10 ++--- 8 files changed, 81 insertions(+), 10 deletions(-) create mode 100644 examples/httpbin/conftest.py diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index a3137538..25059107 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -2,6 +2,13 @@ ## 3.0.11 (2020-06-08) +**Changed** + +- change: override variables + (1) testcase: session variables > step variables > config variables + (2) testsuite: testcase variables > config variables + (3) testsuite testcase variables > testcase config variables + **Fixed** - fix: incorrect summary success when testcase failed diff --git a/examples/httpbin/basic_test.py b/examples/httpbin/basic_test.py index 4fe82d84..aa650da2 100644 --- a/examples/httpbin/basic_test.py +++ b/examples/httpbin/basic_test.py @@ -1,4 +1,4 @@ -# NOTE: Generated By HttpRunner v3.0.10 +# NOTE: Generated By HttpRunner v3.0.11 # FROM: examples/httpbin/basic.yml from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase diff --git a/examples/httpbin/conftest.py b/examples/httpbin/conftest.py new file mode 100644 index 00000000..5bb3d816 --- /dev/null +++ b/examples/httpbin/conftest.py @@ -0,0 +1,64 @@ +# NOTICE: Generated By HttpRunner. +import json +import os +import time + +import pytest +from loguru import logger + +from httprunner.utils import get_platform, ExtendJSONEncoder + + +@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 + + testcase_summary_json = testcase_summary.dict() + testcase_summary_json["records"] = testcase_summary_json.pop("step_datas") + summary["details"].append(testcase_summary_json) + + summary_path = "/Users/debugtalk/MyProjects/HttpRunner-dev/HttpRunner/examples/httpbin/logs/upload.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, ensure_ascii=False, cls=ExtendJSONEncoder) + + logger.info(f"generated task summary: {summary_path}") + diff --git a/examples/httpbin/hooks_test.py b/examples/httpbin/hooks_test.py index 5ae2da61..e090cada 100644 --- a/examples/httpbin/hooks_test.py +++ b/examples/httpbin/hooks_test.py @@ -1,4 +1,4 @@ -# NOTE: Generated By HttpRunner v3.0.10 +# NOTE: Generated By HttpRunner v3.0.11 # FROM: examples/httpbin/hooks.yml from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase diff --git a/examples/httpbin/load_image_test.py b/examples/httpbin/load_image_test.py index cd21fb69..38203afb 100644 --- a/examples/httpbin/load_image_test.py +++ b/examples/httpbin/load_image_test.py @@ -1,4 +1,4 @@ -# NOTE: Generated By HttpRunner v3.0.10 +# NOTE: Generated By HttpRunner v3.0.11 # FROM: examples/httpbin/load_image.yml from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase diff --git a/examples/httpbin/upload_test.py b/examples/httpbin/upload_test.py index fa100f85..6d80b78e 100644 --- a/examples/httpbin/upload_test.py +++ b/examples/httpbin/upload_test.py @@ -1,4 +1,4 @@ -# NOTE: Generated By HttpRunner v3.0.10 +# NOTE: Generated By HttpRunner v3.0.11 # FROM: examples/httpbin/upload.yml from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase diff --git a/examples/httpbin/validate_test.py b/examples/httpbin/validate_test.py index 281a94fb..6826d6dd 100644 --- a/examples/httpbin/validate_test.py +++ b/examples/httpbin/validate_test.py @@ -1,4 +1,4 @@ -# NOTE: Generated By HttpRunner v3.0.10 +# NOTE: Generated By HttpRunner v3.0.11 # FROM: examples/httpbin/validate.yml from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase diff --git a/httprunner/make.py b/httprunner/make.py index b4b24ae1..2e842213 100644 --- a/httprunner/make.py +++ b/httprunner/make.py @@ -453,18 +453,18 @@ def __make(tests_path: Text) -> NoReturn: continue if not isinstance(test_content, Dict): - raise exceptions.FileFormatError( - f"test content not in dict format: {test_content}" - ) + logger.warning(f"test content not in dict format. \npath: {test_file}") + continue # api in v2 format, convert to v3 testcase if "request" in test_content and "name" in test_content: test_content = ensure_testcase_v3_api(test_content) if "config" not in test_content: - raise exceptions.FileFormatError( - f"miss config part in testcase/testsuite: {test_content}" + logger.warning( + f"Invalid testcase/testsuite: missing config part in testcase/testsuite.\npath: {test_file}" ) + continue test_content.setdefault("config", {})["path"] = test_file From 80cbfdc3ca8aeec35fd22e04382702b5f766c147 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Mon, 8 Jun 2020 16:56:55 +0800 Subject: [PATCH 07/10] tests: update tests --- httprunner/scaffold.py | 67 +++++++++++++++++++++++++++++++--------- tests/data/a-b.c/1.yml | 8 ++--- tests/data/a-b.c/2 3.yml | 10 ++---- tests/utils_test.py | 2 +- 4 files changed, 61 insertions(+), 26 deletions(-) diff --git a/httprunner/scaffold.py b/httprunner/scaffold.py index 19450d6e..27dea6e7 100644 --- a/httprunner/scaffold.py +++ b/httprunner/scaffold.py @@ -42,16 +42,20 @@ def create_scaffold(project_name): config: name: "request methods testcase with functions" variables: - foo1: session_bar1 + foo1: config_bar1 + foo2: config_bar2 + expect_foo1: config_bar1 + expect_foo2: config_bar2 base_url: "https://postman-echo.com" verify: False + export: ["foo3"] teststeps: - name: get with params variables: - foo1: bar1 - foo2: session_bar2 + foo1: bar11 + foo2: bar21 sum_v: "${sum_two(1, 2)}" request: method: GET @@ -63,43 +67,78 @@ teststeps: headers: User-Agent: HttpRunner/${get_httprunner_version()} extract: - session_foo2: "body.args.foo2" + foo3: "body.args.foo2" validate: - eq: ["status_code", 200] - - eq: ["body.args.foo1", "session_bar1"] + - eq: ["body.args.foo1", "bar11"] - eq: ["body.args.sum_v", "3"] - - eq: ["body.args.foo2", "session_bar2"] + - eq: ["body.args.foo2", "bar21"] - name: post raw text variables: - foo1: "hello world" - foo3: "$session_foo2" + foo1: "bar12" + foo3: "bar32" 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." + data: "This is expected to be sent back as part of response body: $foo1-$foo2-$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."] + - eq: ["body.data", "This is expected to be sent back as part of response body: bar12-$expect_foo2-bar21."] +- + name: post form data + variables: + foo2: bar23 + request: + method: POST + url: /post + headers: + User-Agent: HttpRunner/${get_httprunner_version()} + Content-Type: "application/x-www-form-urlencoded" + data: "foo1=$foo1&foo2=$foo2&foo3=$foo3" + validate: + - eq: ["status_code", 200] + - eq: ["body.form.foo1", "$expect_foo1"] + - eq: ["body.form.foo2", "bar23"] + - eq: ["body.form.foo3", "bar21"] """ demo_testcase_with_ref_content = """ config: name: "request methods testcase: reference testcase" variables: - foo1: session_bar1 + foo1: testsuite_config_bar1 + expect_foo1: testsuite_config_bar1 + expect_foo2: config_bar2 base_url: "https://postman-echo.com" verify: False teststeps: - - name: request with referenced testcase + name: request with functions variables: - foo1: override_bar1 - # NOTICE: relative testcase path based on debugtalk.py + foo1: testcase_ref_bar1 + expect_foo1: testcase_ref_bar1 testcase: testcases/demo_testcase_request.yml + export: + - foo3 +- + name: post form data + variables: + foo1: bar1 + request: + method: POST + url: /post + headers: + User-Agent: HttpRunner/${get_httprunner_version()} + Content-Type: "application/x-www-form-urlencoded" + data: "foo1=$foo1&foo2=$foo3" + validate: + - eq: ["status_code", 200] + - eq: ["body.form.foo1", "bar1"] + - eq: ["body.form.foo2", "bar21"] """ ignore_content = "\n".join( [".env", "reports/*", "__pycache__/*", "*.pyc", ".python-version", "logs/*"] diff --git a/tests/data/a-b.c/1.yml b/tests/data/a-b.c/1.yml index ffb63d09..5754c338 100644 --- a/tests/data/a-b.c/1.yml +++ b/tests/data/a-b.c/1.yml @@ -1,7 +1,8 @@ config: name: "request methods testcase with functions" variables: - foo1: session_bar1 + foo1: config_bar1 + foo2: config_bar2 base_url: "https://postman-echo.com" verify: False @@ -10,7 +11,6 @@ teststeps: name: get with params variables: foo1: bar1 - foo2: session_bar2 sum_v: "${sum_two(1, 2)}" request: method: GET @@ -25,6 +25,6 @@ teststeps: session_foo2: "body.args.foo2" validate: - eq: ["status_code", 200] - - eq: ["body.args.foo1", "session_bar1"] + - eq: ["body.args.foo1", "bar1"] - eq: ["body.args.sum_v", "3"] - - eq: ["body.args.foo2", "session_bar2"] + - eq: ["body.args.foo2", "config_bar2"] diff --git a/tests/data/a-b.c/2 3.yml b/tests/data/a-b.c/2 3.yml index ef4961ed..f20d5b4d 100644 --- a/tests/data/a-b.c/2 3.yml +++ b/tests/data/a-b.c/2 3.yml @@ -1,22 +1,18 @@ config: name: "reference testcase unittest for abnormal folder path" - variables: - foo1: session_bar1 base_url: "https://postman-echo.com" verify: False teststeps: - name: request with functions - variables: - foo1: override_bar1 testcase: 1.yml export: - session_foo2 - name: post form data variables: - foo1: bar1 + foo1: bar12 request: method: POST url: /post @@ -26,5 +22,5 @@ teststeps: data: "foo1=$foo1&foo2=$session_foo2" validate: - eq: ["status_code", 200] - - eq: ["body.form.foo1", "session_bar1"] - - eq: ["body.form.foo2", "session_bar2"] + - eq: ["body.form.foo1", "bar12"] + - eq: ["body.form.foo2", "config_bar2"] diff --git a/tests/utils_test.py b/tests/utils_test.py index b47961c7..d301587b 100644 --- a/tests/utils_test.py +++ b/tests/utils_test.py @@ -139,5 +139,5 @@ class TestUtils(unittest.TestCase): config_variables = {"base_url": "https://httpbin.org", "foo1": "bar111"} self.assertEqual( override_config_variables(step_variables, config_variables), - {"base_url": "https://httpbin.org", "foo1": "bar111"}, + {"base_url": "https://httpbin.org", "foo1": "bar1"}, ) From a33f689726793c692103d2d357cd49fb439d7f9d Mon Sep 17 00:00:00 2001 From: debugtalk Date: Mon, 8 Jun 2020 17:18:25 +0800 Subject: [PATCH 08/10] fix: reload to refresh previously loaded debugtalk module --- docs/CHANGELOG.md | 1 + httprunner/loader.py | 2 ++ tests/compat_test.py | 5 ++++- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 25059107..06133d0b 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -12,6 +12,7 @@ **Fixed** - fix: incorrect summary success when testcase failed +- fix: reload to refresh previously loaded debugtalk module ## 3.0.10 (2020-06-07) diff --git a/httprunner/loader.py b/httprunner/loader.py index 4982eb92..9592e5bf 100644 --- a/httprunner/loader.py +++ b/httprunner/loader.py @@ -371,6 +371,8 @@ def load_debugtalk_functions() -> Dict[Text, Callable]: """ # load debugtalk.py module imported_module = importlib.import_module("debugtalk") + # reload to refresh previously loaded module + imported_module = importlib.reload(imported_module) return load_module_functions(imported_module) diff --git a/tests/compat_test.py b/tests/compat_test.py index 5e80e6d6..49e78500 100644 --- a/tests/compat_test.py +++ b/tests/compat_test.py @@ -1,11 +1,14 @@ import os import unittest -from httprunner import compat, exceptions +from httprunner import compat, exceptions, loader from httprunner.compat import convert_variables class TestCompat(unittest.TestCase): + def setUp(self): + loader.project_meta = None + def test_convert_variables(self): raw_variables = [{"var1": 1}, {"var2": "val2"}] self.assertEqual( From 2ebc8a63ce41717537332f1f7ae3930d461f1f31 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Mon, 8 Jun 2020 18:05:10 +0800 Subject: [PATCH 09/10] fix: escape 5065 in variable value --- docs/CHANGELOG.md | 1 + httprunner/parser.py | 42 ++++++++++++++++++++++++++++++++++-------- tests/parser_test.py | 15 +++++++++++++++ 3 files changed, 50 insertions(+), 8 deletions(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 06133d0b..93eba206 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -13,6 +13,7 @@ - fix: incorrect summary success when testcase failed - fix: reload to refresh previously loaded debugtalk module +- fix: escape $$ in variable value ## 3.0.10 (2020-06-07) diff --git a/httprunner/parser.py b/httprunner/parser.py index 74f4f69e..00ca4300 100644 --- a/httprunner/parser.py +++ b/httprunner/parser.py @@ -44,11 +44,11 @@ def build_url(base_url, path): raise exceptions.ParamsError("base url missed!") -def regex_findall_variables(content: Text) -> List[Text]: +def regex_findall_variables(raw_string: Text) -> List[Text]: """ extract all variable names from content, which is in format $variable Args: - content (str): string content + raw_string (str): string content Returns: list: variables list extracted from string content @@ -68,14 +68,40 @@ def regex_findall_variables(content: Text) -> List[Text]: """ try: - vars_list = [] - for var_tuple in variable_regex_compile.findall(content): - vars_list.append(var_tuple[0] or var_tuple[1]) - return vars_list - except TypeError as ex: - capture_exception(ex) + match_start_position = raw_string.index("$", 0) + except ValueError: return [] + vars_list = [] + while match_start_position < len(raw_string): + + # Notice: notation priority + # $$ > $var + + # search $$ + dollar_match = dolloar_regex_compile.match(raw_string, match_start_position) + if dollar_match: + match_start_position = dollar_match.end() + continue + + # search variable like ${var} or $var + var_match = variable_regex_compile.match(raw_string, match_start_position) + if var_match: + var_name = var_match.group(1) or var_match.group(2) + vars_list.append(var_name) + match_start_position = var_match.end() + continue + + curr_position = match_start_position + try: + # find next $ location + match_start_position = raw_string.index("$", curr_position + 1) + except ValueError: + # break while loop + break + + return vars_list + def regex_findall_functions(content: Text) -> List[Text]: """ extract all functions from string content, which are in format ${fun()} diff --git a/tests/parser_test.py b/tests/parser_test.py index bfd95c92..e7ac19b4 100644 --- a/tests/parser_test.py +++ b/tests/parser_test.py @@ -3,6 +3,7 @@ import unittest from httprunner import parser from httprunner.exceptions import VariableNotFound, FunctionNotFound +from httprunner.parser import regex_findall_variables class TestParserBasic(unittest.TestCase): @@ -25,6 +26,19 @@ class TestParserBasic(unittest.TestCase): self.assertEqual(parser.parse_string_value("$var"), "$var") self.assertEqual(parser.parse_string_value("${func}"), "${func}") + def test_regex_findall_variables(self): + self.assertEqual(regex_findall_variables("$variable"), ["variable"]) + self.assertEqual(regex_findall_variables("${variable}123"), ["variable"]) + self.assertEqual(regex_findall_variables("/blog/$postid"), ["postid"]) + self.assertEqual(regex_findall_variables("/$var1/$var2"), ["var1", "var2"]) + self.assertEqual(regex_findall_variables("abc"), []) + self.assertEqual(regex_findall_variables("Z:2>1*0*1+1$a"), ["a"]) + self.assertEqual(regex_findall_variables("Z:2>1*0*1+1$$a"), []) + self.assertEqual(regex_findall_variables("Z:2>1*0*1+1$$$a"), ["a"]) + self.assertEqual(regex_findall_variables("Z:2>1*0*1+1$$$$a"), []) + self.assertEqual(regex_findall_variables("Z:2>1*0*1+1$$a$b"), ["b"]) + self.assertEqual(regex_findall_variables("Z:2>1*0*1+1$$a$$b"), []) + def test_extract_variables(self): self.assertEqual(parser.extract_variables("$var"), {"var"}) self.assertEqual(parser.extract_variables("$var123"), {"var123"}) @@ -40,6 +54,7 @@ class TestParserBasic(unittest.TestCase): parser.extract_variables("${gen_md5($TOKEN, $data, $random)}"), {"TOKEN", "data", "random"}, ) + self.assertEqual(parser.extract_variables("Z:2>1*0*1+1$$1"), set()) def test_parse_function_params(self): self.assertEqual(parser.parse_function_params(""), {"args": [], "kwargs": {}}) From deabedc6016045fa62c51ea5a09e980008d0e245 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Mon, 8 Jun 2020 18:09:23 +0800 Subject: [PATCH 10/10] fix: update tests --- examples/httpbin/conftest.py | 64 ------------------------------------ 1 file changed, 64 deletions(-) delete mode 100644 examples/httpbin/conftest.py diff --git a/examples/httpbin/conftest.py b/examples/httpbin/conftest.py deleted file mode 100644 index 5bb3d816..00000000 --- a/examples/httpbin/conftest.py +++ /dev/null @@ -1,64 +0,0 @@ -# NOTICE: Generated By HttpRunner. -import json -import os -import time - -import pytest -from loguru import logger - -from httprunner.utils import get_platform, ExtendJSONEncoder - - -@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 - - testcase_summary_json = testcase_summary.dict() - testcase_summary_json["records"] = testcase_summary_json.pop("step_datas") - summary["details"].append(testcase_summary_json) - - summary_path = "/Users/debugtalk/MyProjects/HttpRunner-dev/HttpRunner/examples/httpbin/logs/upload.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, ensure_ascii=False, cls=ExtendJSONEncoder) - - logger.info(f"generated task summary: {summary_path}") -