From a1e7d0e13cee78b69c24da6fbfe305105e188306 Mon Sep 17 00:00:00 2001 From: buyuxiang Date: Thu, 30 Dec 2021 14:47:10 +0800 Subject: [PATCH 1/3] fix: contains assertion bug; feat: add type_match, contained_by, string_equals assertion methods Change-Id: Icb15fe33a58d1ff69991435bbe70c3cd53bb8dea --- examples/function_test.go | 8 +++++-- internal/builtin/assertion.go | 22 +++++++++++++++++- validate.go | 44 +++++++++++++++++++++++++++++++++++ 3 files changed, 71 insertions(+), 3 deletions(-) diff --git a/examples/function_test.go b/examples/function_test.go index cd2c2d98..44c8c590 100644 --- a/examples/function_test.go +++ b/examples/function_test.go @@ -19,14 +19,18 @@ func TestCaseCallFunction(t *testing.T) { TestSteps: []hrp.IStep{ hrp.NewStep("get with params"). GET("/get"). - WithParams(map[string]interface{}{"foo1": "${gen_random_string($n)}", "foo2": "${max($a, $b)}"}). + WithParams(map[string]interface{}{"foo1": "${gen_random_string($n)}", "foo2": "${max($a, $b)}", "foo3": "Foo3"}). WithHeaders(map[string]string{"User-Agent": "HttpRunnerPlus"}). Extract(). WithJmesPath("body.args.foo1", "varFoo1"). Validate(). AssertEqual("status_code", 200, "check status code"). AssertLengthEqual("body.args.foo1", 5, "check args foo1"). - AssertEqual("body.args.foo2", "12.3", "check args foo2"), // notice: request params value will be converted to string + AssertEqual("body.args.foo2", "12.3", "check args foo2"). + AssertTypeMatch("body.args.foo3", "str", "check args foo3 is type string"). + AssertStringEqual("body.args.foo3", "foo3", "check args foo3 case-insensitivity"). + AssertContains("body.args.foo3", "Foo", "check contains "). + AssertContainedBy("body.args.foo3", "this is Foo3 test", "check contained by"), // notice: request params value will be converted to string hrp.NewStep("post json data with functions"). POST("/post"). WithHeaders(map[string]string{"User-Agent": "HttpRunnerPlus"}). diff --git a/internal/builtin/assertion.go b/internal/builtin/assertion.go index 31110221..87e4709b 100644 --- a/internal/builtin/assertion.go +++ b/internal/builtin/assertion.go @@ -15,13 +15,16 @@ var Assertions = map[string]func(t assert.TestingT, expected interface{}, actual "greater_or_equals": assert.GreaterOrEqual, "less_or_equals": assert.LessOrEqual, "not_equal": assert.NotEqual, - "contains": assert.Contains, + "contained_by": assert.Contains, "regex_match": assert.Regexp, + "type_match": assert.IsType, // custom assertions "startswith": StartsWith, // check if string starts with substring "endswith": EndsWith, // check if string ends with substring "length_equals": EqualLength, "length_equal": EqualLength, // alias for length_equals + "contains": Contains, + "string_equals": EqualString, } func StartsWith(t assert.TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { @@ -83,3 +86,20 @@ func convertInt(value interface{}) (int, error) { return 0, fmt.Errorf("unsupported int convertion for %v(%T)", v, v) } } + +// Contains assert whether actual +func Contains(t assert.TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { + return assert.Contains(t, actual, expected, msgAndArgs) +} + +func EqualString(t assert.TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { + if !assert.IsType(t, "string", actual, msgAndArgs) { + return false + } + if !assert.IsType(t, "string", expected, msgAndArgs) { + return false + } + actualString := actual.(string) + expectedString := expected.(string) + return assert.True(t, strings.EqualFold(actualString, expectedString), msgAndArgs) +} diff --git a/validate.go b/validate.go index f5d67a8a..9748ac05 100644 --- a/validate.go +++ b/validate.go @@ -35,6 +35,28 @@ func (s *StepRequestValidation) AssertEqual(jmesPath string, expected interface{ return s } +func (s *StepRequestValidation) AssertContains(jmesPath string, expected interface{}, msg string) *StepRequestValidation { + v := Validator{ + Check: jmesPath, + Assert: "contains", + Expect: expected, + Message: msg, + } + s.step.Validators = append(s.step.Validators, v) + return s +} + +func (s *StepRequestValidation) AssertTypeMatch(jmesPath string, expected interface{}, msg string) *StepRequestValidation { + v := Validator{ + Check: jmesPath, + Assert: "type_match", + Expect: expected, + Message: msg, + } + s.step.Validators = append(s.step.Validators, v) + return s +} + func (s *StepRequestValidation) AssertStartsWith(jmesPath string, expected interface{}, msg string) *StepRequestValidation { v := Validator{ Check: jmesPath, @@ -67,3 +89,25 @@ func (s *StepRequestValidation) AssertLengthEqual(jmesPath string, expected inte s.step.Validators = append(s.step.Validators, v) return s } + +func (s *StepRequestValidation) AssertContainedBy(jmesPath string, expected interface{}, msg string) *StepRequestValidation { + v := Validator{ + Check: jmesPath, + Assert: "contained_by", + Expect: expected, + Message: msg, + } + s.step.Validators = append(s.step.Validators, v) + return s +} + +func (s *StepRequestValidation) AssertStringEqual(jmesPath string, expected interface{}, msg string) *StepRequestValidation { + v := Validator{ + Check: jmesPath, + Assert: "string_equals", + Expect: expected, + Message: msg, + } + s.step.Validators = append(s.step.Validators, v) + return s +} \ No newline at end of file From e3b481a273e34f411de1d57fa219852b832160db Mon Sep 17 00:00:00 2001 From: buyuxiang Date: Thu, 30 Dec 2021 18:27:58 +0800 Subject: [PATCH 2/3] complete the comment of builtin.Contains Change-Id: I949e2ccda53960dd12f64bda3c9c69513f59d4ed --- internal/builtin/assertion.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/builtin/assertion.go b/internal/builtin/assertion.go index 87e4709b..20813392 100644 --- a/internal/builtin/assertion.go +++ b/internal/builtin/assertion.go @@ -87,7 +87,7 @@ func convertInt(value interface{}) (int, error) { } } -// Contains assert whether actual +// Contains assert whether actual element contains expected element func Contains(t assert.TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { return assert.Contains(t, actual, expected, msgAndArgs) } From e5608f77581dc8e67aab1d3bc938d2ddb7fe80da Mon Sep 17 00:00:00 2001 From: buyuxiang Date: Thu, 30 Dec 2021 19:50:55 +0800 Subject: [PATCH 3/3] chore: add BUILTIN.md, append CHANGELOG.md Change-Id: I12e0c6ba3621d383fa46a811abbdcd55dbf80a17 --- docs/BUILTIN.md | 44 ++++++++++++++++++++++++++++++++++++ docs/CHANGELOG.md | 2 ++ internal/builtin/function.go | 2 +- 3 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 docs/BUILTIN.md diff --git a/docs/BUILTIN.md b/docs/BUILTIN.md new file mode 100644 index 00000000..e90b0a04 --- /dev/null +++ b/docs/BUILTIN.md @@ -0,0 +1,44 @@ +# Builtin + +## Assertion Methods + +### Usage +In "teststeps" of each json/yaml testcase, the "validate" part contains four fields: "check", "assert", "expect" and +"msg", when using assertion methods, method name should be put in "assert" field. The assertion result of "check" +element will be checked out using the regulation you put in "assert" field and compared with the element in "expect" +field. + +### Method List + +- equals: assert the element to check equals the expected element. +- equal: alias for equals. +- greater_than: assert the element to check is greater than the expected element. +- less_than: assert the element to check is less than the expected element. +- greater_or_equals: assert the element to check is greater than or equal with the expected element. +- less_or_equals: assert the element to check is less than or equal with the expected element. +- not_equal: assert the element to check is not equal with the expected element. +- contained_by: assert the expected element contains the element to check. +- regex_match: assert the element to check matches the expected element using regex. +- type_match: assert the element to check matches the expected element in type. +- startswith: assert the element to check starts with the expected element. +- endswith: assert the element to check ends with the expected element. +- length_equals: assert the length of the element to check is equal with the expected element. +- length_equal: alias for length_equals. +- contains: assert the element to check contains the expected element. +- string_equals: assert the string is equal with the expected string. + +## Common Functions + +### Usage +The common functions are useful during the variables configuration, you can use "${FUNCTION_NAME}" to call the specific +function to define variables. + +### Function List +- get_timestamp: get the thirteen-digit timestamp of current time. (call without argument) +- sleep: sleep n seconds to simulate the thinking time. (call with one argument n) +- gen_random_string: get the n-digit random string. (call with one argument n) +- max: get the maximum of two numbers m and n. (call with two argument m and n) +- md5: get the MD5 of the input string s. (call with one argument s) + + + diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 67354670..d428e4f1 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -1,5 +1,7 @@ # Release History +-feat: add more plentiful response assertion methods + ## v0.3.0 (2021-12-22) - feat: implement `transaction` mechanism for load test diff --git a/internal/builtin/function.go b/internal/builtin/function.go index d1d3dd3b..e11529eb 100644 --- a/internal/builtin/function.go +++ b/internal/builtin/function.go @@ -13,7 +13,7 @@ var Functions = map[string]interface{}{ "sleep": sleep, // call with one argument "gen_random_string": genRandomString, // call with one argument "max": math.Max, // call with two arguments - "md5": MD5, + "md5": MD5, // call with one argument } func init() {