From dd1cd5938f278de67505e4fdf4b27c02f351b086 Mon Sep 17 00:00:00 2001 From: buyuxiang <347586493@qq.com> Date: Thu, 26 May 2022 16:04:46 +0800 Subject: [PATCH] update: httprunner make v4 --- hrp/internal/convert/converter.go | 5 +- hrp/internal/convert/converter_har.go | 32 +-------- hrp/internal/convert/converter_json.go | 27 +------- hrp/internal/convert/converter_postman.go | 35 +--------- hrp/internal/convert/converter_yaml.go | 27 +------- hrp/step_api.go | 2 +- hrp/testcase.go | 83 ++--------------------- httprunner/compat.py | 32 ++++++--- httprunner/compat_test.py | 55 +++++++++++++-- httprunner/make.py | 16 ++--- 10 files changed, 95 insertions(+), 219 deletions(-) diff --git a/hrp/internal/convert/converter.go b/hrp/internal/convert/converter.go index 8733614c..63e07690 100644 --- a/hrp/internal/convert/converter.go +++ b/hrp/internal/convert/converter.go @@ -241,7 +241,6 @@ func (c *TCaseConverter) ToGoTest() (string, error) { type ICaseConverter interface { Struct() *TCaseConverter ToJSON() (string, error) - ToJSONTemp() (string, error) ToYAML() (string, error) ToGoTest() (string, error) ToPyTest() (string, error) @@ -345,8 +344,8 @@ func makeTestCaseFromJSONYAML(iCaseConverter ICaseConverter) (*hrp.TCase, error) } func convertToPyTest(iCaseConverter ICaseConverter) (string, error) { - // convert to temporary json testcase compatible with python engine style - jsonPath, err := iCaseConverter.ToJSONTemp() + // convert to temporary json testcase + jsonPath, err := iCaseConverter.ToJSON() inputType := iCaseConverter.Struct().InputType if err != nil { return "", errors.Wrapf(err, "(%s -> pytest step 1) failed to convert to temporary json testcase", inputType.String()) diff --git a/hrp/internal/convert/converter_har.go b/hrp/internal/convert/converter_har.go index 1ee513ae..d35a9031 100644 --- a/hrp/internal/convert/converter_har.go +++ b/hrp/internal/convert/converter_har.go @@ -384,19 +384,6 @@ func (c *ConverterHAR) ToJSON() (string, error) { return jsonPath, nil } -func (c *ConverterHAR) ToJSONTemp() (string, error) { - tCase, err := c.makeTestCaseTemp() - if err != nil { - return "", err - } - jsonPath := c.converter.genOutputPath(suffixJSON) - err = builtin.Dump2JSON(tCase, jsonPath) - if err != nil { - return "", err - } - return jsonPath, nil -} - func (c *ConverterHAR) ToYAML() (string, error) { tCase, err := c.makeTestCase() if err != nil { @@ -429,24 +416,7 @@ func (c *ConverterHAR) makeTestCase() (*hrp.TCase, error) { Config: c.prepareConfig(), TestSteps: teststeps, } - err = tCase.MakeCompat2GoEngine() - if err != nil { - return nil, err - } - return tCase, nil -} - -func (c *ConverterHAR) makeTestCaseTemp() (*hrp.TCase, error) { - teststeps, err := c.prepareTestSteps() - if err != nil { - return nil, err - } - - tCase := &hrp.TCase{ - Config: c.prepareConfig(), - TestSteps: teststeps, - } - err = tCase.MakeCompat2PyEngine() + err = tCase.MakeCompat() if err != nil { return nil, err } diff --git a/hrp/internal/convert/converter_json.go b/hrp/internal/convert/converter_json.go index 5aa0b69a..fc380142 100644 --- a/hrp/internal/convert/converter_json.go +++ b/hrp/internal/convert/converter_json.go @@ -37,19 +37,6 @@ func (c *ConverterJSON) ToJSON() (string, error) { return jsonPath, nil } -func (c *ConverterJSON) ToJSONTemp() (string, error) { - testCase, err := c.makeTestCaseTemp() - if err != nil { - return "", err - } - jsonPath := c.converter.genOutputPath(suffixJSON) - err = builtin.Dump2JSON(testCase, jsonPath) - if err != nil { - return "", err - } - return jsonPath, nil -} - func (c *ConverterJSON) ToYAML() (string, error) { testCase, err := c.makeTestCase() if err != nil { @@ -91,19 +78,7 @@ func (c *ConverterJSON) makeTestCase() (*hrp.TCase, error) { if err != nil { return nil, err } - err = tCase.MakeCompat2GoEngine() - if err != nil { - return nil, err - } - return tCase, nil -} - -func (c *ConverterJSON) makeTestCaseTemp() (*hrp.TCase, error) { - tCase, err := makeTestCaseFromJSONYAML(c) - if err != nil { - return nil, err - } - err = tCase.MakeCompat2PyEngine() + err = tCase.MakeCompat() if err != nil { return nil, err } diff --git a/hrp/internal/convert/converter_postman.go b/hrp/internal/convert/converter_postman.go index bb746e87..bfa9a19e 100644 --- a/hrp/internal/convert/converter_postman.go +++ b/hrp/internal/convert/converter_postman.go @@ -144,19 +144,6 @@ func (c *ConverterPostman) ToJSON() (string, error) { return jsonPath, nil } -func (c *ConverterPostman) ToJSONTemp() (string, error) { - testCase, err := c.makeTestCaseTemp() - if err != nil { - return "", err - } - jsonPath := c.converter.genOutputPath(suffixJSON) - err = builtin.Dump2JSON(testCase, jsonPath) - if err != nil { - return "", err - } - return jsonPath, nil -} - func (c *ConverterPostman) ToYAML() (string, error) { testCase, err := c.makeTestCase() if err != nil { @@ -192,27 +179,7 @@ func (c *ConverterPostman) makeTestCase() (*hrp.TCase, error) { Config: c.prepareConfig(casePostman), TestSteps: teststeps, } - err = tCase.MakeCompat2GoEngine() - if err != nil { - return nil, err - } - return tCase, nil -} - -func (c *ConverterPostman) makeTestCaseTemp() (*hrp.TCase, error) { - casePostman, err := c.load() - if err != nil { - return nil, err - } - teststeps, err := c.prepareTestSteps(casePostman) - if err != nil { - return nil, err - } - tCase := &hrp.TCase{ - Config: c.prepareConfig(casePostman), - TestSteps: teststeps, - } - err = tCase.MakeCompat2PyEngine() + err = tCase.MakeCompat() if err != nil { return nil, err } diff --git a/hrp/internal/convert/converter_yaml.go b/hrp/internal/convert/converter_yaml.go index 81d1b1a9..2ad783b1 100644 --- a/hrp/internal/convert/converter_yaml.go +++ b/hrp/internal/convert/converter_yaml.go @@ -34,19 +34,6 @@ func (c *ConverterYAML) ToJSON() (string, error) { return jsonPath, nil } -func (c *ConverterYAML) ToJSONTemp() (string, error) { - testCase, err := c.makeTestCaseTemp() - if err != nil { - return "", err - } - jsonPath := c.converter.genOutputPath(suffixJSON) - err = builtin.Dump2JSON(testCase, jsonPath) - if err != nil { - return "", err - } - return jsonPath, nil -} - func (c *ConverterYAML) ToYAML() (string, error) { testCase, err := c.makeTestCase() if err != nil { @@ -74,19 +61,7 @@ func (c *ConverterYAML) makeTestCase() (*hrp.TCase, error) { if err != nil { return nil, err } - err = tCase.MakeCompat2GoEngine() - if err != nil { - return nil, err - } - return tCase, nil -} - -func (c *ConverterYAML) makeTestCaseTemp() (*hrp.TCase, error) { - tCase, err := makeTestCaseFromJSONYAML(c) - if err != nil { - return nil, err - } - err = tCase.MakeCompat2PyEngine() + err = tCase.MakeCompat() if err != nil { return nil, err } diff --git a/hrp/step_api.go b/hrp/step_api.go index 7b57d896..1c9992ba 100644 --- a/hrp/step_api.go +++ b/hrp/step_api.go @@ -47,7 +47,7 @@ func (path *APIPath) ToAPI() (*API, error) { if err != nil { return nil, err } - err = convertValidatorCompat2GoEngine(api.Validators) + err = convertCompatValidator(api.Validators) convertExtract(api.Extract) return api, err } diff --git a/hrp/testcase.go b/hrp/testcase.go index 6548ab55..4182cec7 100644 --- a/hrp/testcase.go +++ b/hrp/testcase.go @@ -64,7 +64,7 @@ func (path *TestCasePath) ToTestCase() (*TestCase, error) { return nil, errors.New("incorrect testcase file format, expected config in file") } - err = tc.MakeCompat2GoEngine() + err = tc.MakeCompat() if err != nil { return nil, err } @@ -173,11 +173,11 @@ type TCase struct { TestSteps []*TStep `json:"teststeps" yaml:"teststeps"` } -// MakeCompat2GoEngine converts TCase compatible with Golang engine style -func (tc *TCase) MakeCompat2GoEngine() (err error) { +// MakeCompat converts TCase compatible with Golang engine style +func (tc *TCase) MakeCompat() (err error) { defer func() { if p := recover(); p != nil { - err = fmt.Errorf("[MakeCompat2GoEngine] convert compat testcase error: %v", p) + err = fmt.Errorf("[MakeCompat] convert compat testcase error: %v", p) } }() for _, step := range tc.TestSteps { @@ -194,7 +194,7 @@ func (tc *TCase) MakeCompat2GoEngine() (err error) { } // 2. deal with validators compatibility - err = convertValidatorCompat2GoEngine(step.Validators) + err = convertCompatValidator(step.Validators) if err != nil { return err } @@ -205,7 +205,7 @@ func (tc *TCase) MakeCompat2GoEngine() (err error) { return nil } -func convertValidatorCompat2GoEngine(Validators []interface{}) (err error) { +func convertCompatValidator(Validators []interface{}) (err error) { for i, iValidator := range Validators { if _, ok := iValidator.(Validator); ok { continue @@ -272,77 +272,6 @@ func convertCheckExpr(checkExpr string) string { return strings.Join(checkItems, ".") } -// MakeCompat2PyEngine converts TCase compatible with Python engine style -func (tc *TCase) MakeCompat2PyEngine() (err error) { - defer func() { - if p := recover(); p != nil { - err = fmt.Errorf("[MakeCompat2PyEngine] convert compat testcase error: %v", p) - } - }() - for _, step := range tc.TestSteps { - // 1. deal with request body compatibility - if step.Request != nil && step.Request.Body != nil { - if strings.HasPrefix(step.Request.Headers["Content-Type"], "application/json") { - step.Request.Json = step.Request.Body - step.Request.Body = nil - continue - } - step.Request.Data = step.Request.Body - step.Request.Body = nil - } - - // 2. deal with validators compatibility - err = convertValidatorCompat2PyEngine(step.Validators) - if err != nil { - return err - } - } - return -} - -func convertValidatorCompat2PyEngine(Validators []interface{}) (err error) { - for i, iValidator := range Validators { - if v, ok := iValidator.(Validator); ok { - var iValidatorContent []interface{} - iValidatorContent = append(iValidatorContent, v.Check) - iValidatorContent = append(iValidatorContent, v.Expect) - newValidatorMap := make(map[string]interface{}) - newValidatorMap[v.Assert] = iValidatorContent - Validators[i] = newValidatorMap - continue - } - validatorMap := iValidator.(map[string]interface{}) - // validator check priority: Python > Golang engine style - if len(validatorMap) == 1 { - // Python engine style - for _, iValidatorContent := range validatorMap { - validatorContent := iValidatorContent.([]interface{}) - if len(validatorContent) > 3 { - return fmt.Errorf("unexpected validator format: %v", validatorMap) - } - } - continue - } - iCheck, checkExisted := validatorMap["check"] - iAssert, assertExisted := validatorMap["assert"] - iExpect, expectExisted := validatorMap["expect"] - if checkExisted && assertExisted && expectExisted { - // Golang engine style - var validatorContent []interface{} - validatorContent = append(validatorContent, iCheck) - validatorContent = append(validatorContent, iExpect) - if iMsg, msgExisted := validatorMap["msg"]; msgExisted { - validatorContent = append(validatorContent, iMsg) - } - newValidatorMap := make(map[string]interface{}) - newValidatorMap[iAssert.(string)] = validatorContent - Validators[i] = newValidatorMap - continue - } - return fmt.Errorf("unexpected validator format: %v", validatorMap) - } - return -} func LoadTestCases(iTestCases ...ITestCase) ([]*TestCase, error) { testCases := make([]*TestCase, 0) diff --git a/httprunner/compat.py b/httprunner/compat.py index c7352f6a..4732783a 100644 --- a/httprunner/compat.py +++ b/httprunner/compat.py @@ -1,5 +1,5 @@ """ -This module handles compatibility issues between testcase format v2 and v3. +This module handles compatibility issues between testcase format v2, v3 and v4. """ import os import sys @@ -14,9 +14,8 @@ from httprunner.utils import sort_dict_by_custom_order def convert_variables( - raw_variables: Union[Dict, Text], test_path: Text + raw_variables: Union[Dict, Text], test_path: Text ) -> Dict[Text, Any]: - if isinstance(raw_variables, Dict): return raw_variables @@ -33,6 +32,18 @@ def convert_variables( ) +def _convert_request(request: Dict) -> Dict: + if "body" in request: + content_type = "" + if "headers" in request and "Content-Type" in request["headers"]: + content_type = request["headers"]["Content-Type"] + if content_type.startswith("application/json"): + request["json"] = request.pop("body") + else: + request["data"] = request.pop("body") + return _sort_request_by_custom_order(request) + + def _convert_jmespath(raw: Text) -> Text: if not isinstance(raw, Text): raise exceptions.TestCaseFormatError(f"Invalid jmespath extractor: {raw}") @@ -153,6 +164,9 @@ def _ensure_step_attachment(step: Dict) -> Dict: "name": step["name"], } + if "request" in step: + test_dict["request"] = _convert_request(step["request"]) + if "variables" in step: test_dict["variables"] = step["variables"] @@ -181,11 +195,11 @@ def _ensure_step_attachment(step: Dict) -> Dict: return test_dict -def ensure_testcase_v3_api(api_content: Dict) -> Dict: - logger.info("convert api in v2 to testcase format v3") +def ensure_testcase_v4_api(api_content: Dict) -> Dict: + logger.info("convert api in v2/v3 to testcase format v4") teststep = { - "request": _sort_request_by_custom_order(api_content["request"]), + "request": _convert_request(api_content["request"]), } teststep.update(_ensure_step_attachment(api_content)) @@ -202,8 +216,8 @@ def ensure_testcase_v3_api(api_content: Dict) -> Dict: } -def ensure_testcase_v3(test_content: Dict) -> Dict: - logger.info("ensure compatibility with testcase format v2") +def ensure_testcase_v4(test_content: Dict) -> Dict: + logger.info("ensure compatibility with testcase format v2/v3") v3_content = {"config": test_content["config"], "teststeps": []} @@ -221,7 +235,7 @@ def ensure_testcase_v3(test_content: Dict) -> Dict: teststep = {} if "request" in step: - teststep["request"] = _sort_request_by_custom_order(step.pop("request")) + pass elif "api" in step: teststep["testcase"] = step.pop("api") elif "testcase" in step: diff --git a/httprunner/compat_test.py b/httprunner/compat_test.py index 391133a1..a7877fb6 100644 --- a/httprunner/compat_test.py +++ b/httprunner/compat_test.py @@ -26,6 +26,53 @@ class TestCompat(unittest.TestCase): with self.assertRaises(exceptions.TestCaseFormatError): compat.convert_variables(None, "examples/data/a-b.c/1.yml") + def test_convert_request(self): + request_with_json_body = { + "method": "POST", + "url": "https://postman-echo.com/post", + "headers": { + "Content-Type": "application/json" + }, + "body": { + "k1": "v1", + "k2": "v2" + } + } + self.assertEqual( + compat._convert_request(request_with_json_body), + { + "method": "POST", + "url": "https://postman-echo.com/post", + "headers": { + "Content-Type": "application/json" + }, + "json": { + "k1": "v1", + "k2": "v2" + } + } + ) + + request_with_text_body = { + "method": "POST", + "url": "https://postman-echo.com/post", + "headers": { + "Content-Type": "text/plain" + }, + "body": "have a nice day" + } + self.assertEqual( + compat._convert_request(request_with_text_body), + { + "method": "POST", + "url": "https://postman-echo.com/post", + "headers": { + "Content-Type": "text/plain" + }, + "data": "have a nice day" + } + ) + def test_convert_jmespath(self): self.assertEqual(compat._convert_jmespath("content.abc"), "body.abc") self.assertEqual(compat._convert_jmespath("json.abc"), "body.abc") @@ -85,7 +132,7 @@ class TestCompat(unittest.TestCase): [{"eq": ["body[0].name", 201]}], ) - def test_ensure_testcase_v3_api(self): + def test_ensure_testcase_v4_api(self): api_content = { "name": "get with params", "request": { @@ -98,7 +145,7 @@ class TestCompat(unittest.TestCase): "validate": [{"eq": ["content.varB", 200]}, {"lt": ["json.0.varC", 0]}], } self.assertEqual( - compat.ensure_testcase_v3_api(api_content), + compat.ensure_testcase_v4_api(api_content), { "config": { "name": "get with params", @@ -126,7 +173,7 @@ class TestCompat(unittest.TestCase): }, ) - def test_ensure_testcase_v3(self): + def test_ensure_testcase_v4(self): testcase_content = { "config": {"name": "xxx", "base_url": "https://httpbin.org"}, "teststeps": [ @@ -150,7 +197,7 @@ class TestCompat(unittest.TestCase): ], } self.assertEqual( - compat.ensure_testcase_v3(testcase_content), + compat.ensure_testcase_v4(testcase_content), { "config": {"name": "xxx", "base_url": "https://httpbin.org"}, "teststeps": [ diff --git a/httprunner/make.py b/httprunner/make.py index 75a4e783..7fe473b3 100644 --- a/httprunner/make.py +++ b/httprunner/make.py @@ -11,8 +11,8 @@ from httprunner import __version__, exceptions from httprunner.compat import ( convert_variables, ensure_path_sep, - ensure_testcase_v3, - ensure_testcase_v3_api, + ensure_testcase_v4, + ensure_testcase_v4_api, ) from httprunner.loader import ( convert_relative_project_root_dir, @@ -332,8 +332,8 @@ def make_teststep_chain_style(teststep: Dict) -> Text: def make_testcase(testcase: Dict, dir_path: Text = None) -> Text: """convert valid testcase dict to pytest file path""" - # ensure compatibility with testcase format v2 - testcase = ensure_testcase_v3(testcase) + # ensure compatibility with testcase format v2/v3 + testcase = ensure_testcase_v4(testcase) # validate testcase format load_testcase(testcase) @@ -373,9 +373,9 @@ def make_testcase(testcase: Dict, dir_path: Text = None) -> Text: if not isinstance(test_content, Dict): raise exceptions.TestCaseFormatError(f"Invalid teststep: {teststep}") - # api in v2 format, convert to v3 testcase + # api in v2/v3 format, convert to v4 testcase if "request" in test_content and "name" in test_content: - test_content = ensure_testcase_v3_api(test_content) + test_content = ensure_testcase_v4_api(test_content) test_content.setdefault("config", {})["path"] = ref_testcase_path ref_testcase_python_abs_path = make_testcase(test_content) @@ -473,9 +473,9 @@ def __make(tests_path: Text): ) continue - # api in v2 format, convert to v3 testcase + # api in v2/v3 format, convert to v4 testcase if "request" in test_content and "name" in test_content: - test_content = ensure_testcase_v3_api(test_content) + test_content = ensure_testcase_v4_api(test_content) if "config" not in test_content: logger.warning(