Merge pull request #34 from httprunner/refactor-models

v0.2.2

- refactor: update models to make API more concise
- change: remove mkdocs, move to [repo](https://github.com/httprunner/httprunner.github.io)
This commit is contained in:
debugtalk
2021-12-07 18:44:31 +08:00
committed by GitHub
24 changed files with 305 additions and 265 deletions
+3 -3
View File
@@ -58,16 +58,16 @@ func (b *Boomer) convertBoomerTask(testcase *TestCase) *boomer.Task {
Weight: testcase.Config.Weight, Weight: testcase.Config.Weight,
Fn: func() { Fn: func() {
runner := NewRunner(nil).SetDebug(b.debug) runner := NewRunner(nil).SetDebug(b.debug)
config := &testcase.Config config := testcase.Config
for _, step := range testcase.TestSteps { for _, step := range testcase.TestSteps {
var err error var err error
start := time.Now() start := time.Now()
stepData, err := runner.runStep(step, config) stepData, err := runner.runStep(step, config)
elapsed := time.Since(start).Nanoseconds() / int64(time.Millisecond) elapsed := time.Since(start).Nanoseconds() / int64(time.Millisecond)
if err == nil { if err == nil {
b.RecordSuccess(step.Type(), step.Name(), elapsed, stepData.ResponseLength) b.RecordSuccess(step.getType(), step.name(), elapsed, stepData.responseLength)
} else { } else {
b.RecordFailure(step.Type(), step.Name(), elapsed, err.Error()) b.RecordFailure(step.getType(), step.name(), elapsed, err.Error())
} }
} }
}, },
+4 -7
View File
@@ -7,22 +7,19 @@ import (
func TestBoomerStandaloneRun(t *testing.T) { func TestBoomerStandaloneRun(t *testing.T) {
testcase1 := &TestCase{ testcase1 := &TestCase{
Config: TConfig{ Config: NewConfig("TestCase1").SetBaseURL("http://httpbin.org"),
Name: "TestCase1",
BaseURL: "http://httpbin.org",
},
TestSteps: []IStep{ TestSteps: []IStep{
Step("headers"). NewStep("headers").
GET("/headers"). GET("/headers").
Validate(). Validate().
AssertEqual("status_code", 200, "check status code"). AssertEqual("status_code", 200, "check status code").
AssertEqual("headers.\"Content-Type\"", "application/json", "check http response Content-Type"), AssertEqual("headers.\"Content-Type\"", "application/json", "check http response Content-Type"),
Step("user-agent"). NewStep("user-agent").
GET("/user-agent"). GET("/user-agent").
Validate(). Validate().
AssertEqual("status_code", 200, "check status code"). AssertEqual("status_code", 200, "check status code").
AssertEqual("headers.\"Content-Type\"", "application/json", "check http response Content-Type"), AssertEqual("headers.\"Content-Type\"", "application/json", "check http response Content-Type"),
Step("TestCase3").CallRefCase(&TestCase{Config: TConfig{Name: "TestCase3"}}), NewStep("TestCase3").CallRefCase(&TestCase{Config: NewConfig("TestCase3")}),
}, },
} }
testcase2 := &TestCasePath{demoTestCaseJSONPath} testcase2 := &TestCasePath{demoTestCaseJSONPath}
+1 -1
View File
@@ -15,7 +15,7 @@ func (tc *TestCase) ToTCase() (*TCase, error) {
Config: tc.Config, Config: tc.Config,
} }
for _, step := range tc.TestSteps { for _, step := range tc.TestSteps {
tCase.TestSteps = append(tCase.TestSteps, step.ToStruct()) tCase.TestSteps = append(tCase.TestSteps, step.toStruct())
} }
return &tCase, nil return &tCase, nil
} }
+2 -1
View File
@@ -1,7 +1,8 @@
# Release History # Release History
## v0.2.2 (2021-12-06) ## v0.2.2 (2021-12-07)
- refactor: update models to make API more concise
- change: remove mkdocs, move to [repo](https://github.com/httprunner/httprunner.github.io) - change: remove mkdocs, move to [repo](https://github.com/httprunner/httprunner.github.io)
## v0.2.1 (2021-12-02) ## v0.2.1 (2021-12-02)
+1 -1
View File
@@ -22,4 +22,4 @@ Copyright 2021 debugtalk
* [hrp har2case](hrp_har2case.md) - Convert HAR to json/yaml testcase files * [hrp har2case](hrp_har2case.md) - Convert HAR to json/yaml testcase files
* [hrp run](hrp_run.md) - run API test * [hrp run](hrp_run.md) - run API test
###### Auto generated by spf13/cobra on 2-Dec-2021 ###### Auto generated by spf13/cobra on 7-Dec-2021
+1 -1
View File
@@ -39,4 +39,4 @@ hrp boom [flags]
* [hrp](hrp.md) - One-stop solution for HTTP(S) testing. * [hrp](hrp.md) - One-stop solution for HTTP(S) testing.
###### Auto generated by spf13/cobra on 2-Dec-2021 ###### Auto generated by spf13/cobra on 7-Dec-2021
+1 -1
View File
@@ -23,4 +23,4 @@ hrp har2case harPath... [flags]
* [hrp](hrp.md) - One-stop solution for HTTP(S) testing. * [hrp](hrp.md) - One-stop solution for HTTP(S) testing.
###### Auto generated by spf13/cobra on 2-Dec-2021 ###### Auto generated by spf13/cobra on 7-Dec-2021
+1 -1
View File
@@ -30,4 +30,4 @@ hrp run path... [flags]
* [hrp](hrp.md) - One-stop solution for HTTP(S) testing. * [hrp](hrp.md) - One-stop solution for HTTP(S) testing.
###### Auto generated by spf13/cobra on 2-Dec-2021 ###### Auto generated by spf13/cobra on 7-Dec-2021
+7 -9
View File
@@ -8,19 +8,17 @@ import (
) )
var demoTestCase = &hrp.TestCase{ var demoTestCase = &hrp.TestCase{
Config: hrp.TConfig{ Config: hrp.NewConfig("demo with complex mechanisms").
Name: "demo with complex mechanisms", SetBaseURL("https://postman-echo.com").
BaseURL: "https://postman-echo.com", WithVariables(map[string]interface{}{ // global level variables
Variables: map[string]interface{}{ // global level variables
"n": 5, "n": 5,
"a": 12.3, "a": 12.3,
"b": 3.45, "b": 3.45,
"varFoo1": "${gen_random_string($n)}", "varFoo1": "${gen_random_string($n)}",
"varFoo2": "${max($a, $b)}", // 12.3; eval with built-in function "varFoo2": "${max($a, $b)}", // 12.3; eval with built-in function
}, }),
},
TestSteps: []hrp.IStep{ TestSteps: []hrp.IStep{
hrp.Step("get with params"). hrp.NewStep("get with params").
WithVariables(map[string]interface{}{ // step level variables WithVariables(map[string]interface{}{ // step level variables
"n": 3, // inherit config level variables if not set in step level, a/varFoo1 "n": 3, // inherit config level variables if not set in step level, a/varFoo1
"b": 34.5, // override config level variable if existed, n/b/varFoo2 "b": 34.5, // override config level variable if existed, n/b/varFoo2
@@ -37,7 +35,7 @@ var demoTestCase = &hrp.TestCase{
AssertLengthEqual("body.args.foo1", 5, "check args foo1"). // validate response body with jmespath AssertLengthEqual("body.args.foo1", 5, "check args foo1"). // validate response body with jmespath
AssertLengthEqual("$varFoo1", 5, "check args foo1"). // assert with extracted variable from current step AssertLengthEqual("$varFoo1", 5, "check args foo1"). // assert with extracted variable from current step
AssertEqual("body.args.foo2", "34.5", "check args foo2"), // notice: request params value will be converted to string AssertEqual("body.args.foo2", "34.5", "check args foo2"), // notice: request params value will be converted to string
hrp.Step("post json data"). hrp.NewStep("post json data").
POST("/post"). POST("/post").
WithBody(map[string]interface{}{ WithBody(map[string]interface{}{
"foo1": "$varFoo1", // reference former extracted variable "foo1": "$varFoo1", // reference former extracted variable
@@ -47,7 +45,7 @@ var demoTestCase = &hrp.TestCase{
AssertEqual("status_code", 200, "check status code"). AssertEqual("status_code", 200, "check status code").
AssertLengthEqual("body.json.foo1", 5, "check args foo1"). AssertLengthEqual("body.json.foo1", 5, "check args foo1").
AssertEqual("body.json.foo2", 12.3, "check args foo2"), AssertEqual("body.json.foo2", 12.3, "check args foo2"),
hrp.Step("post form data"). hrp.NewStep("post form data").
POST("/post"). POST("/post").
WithHeaders(map[string]string{"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"}). WithHeaders(map[string]string{"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"}).
WithBody(map[string]interface{}{ WithBody(map[string]interface{}{
+9 -13
View File
@@ -9,13 +9,11 @@ import (
// reference extracted variables for validation in the same step // reference extracted variables for validation in the same step
func TestCaseExtractStepSingle(t *testing.T) { func TestCaseExtractStepSingle(t *testing.T) {
testcase := &hrp.TestCase{ testcase := &hrp.TestCase{
Config: hrp.TConfig{ Config: hrp.NewConfig("run request with variables").
Name: "run request with variables", SetBaseURL("https://postman-echo.com").
BaseURL: "https://postman-echo.com", SetVerifySSL(false),
Verify: false,
},
TestSteps: []hrp.IStep{ TestSteps: []hrp.IStep{
hrp.Step("get with params"). hrp.NewStep("get with params").
WithVariables(map[string]interface{}{ WithVariables(map[string]interface{}{
"var1": "bar1", "var1": "bar1",
"agent": "HttpRunnerPlus", "agent": "HttpRunnerPlus",
@@ -46,13 +44,11 @@ func TestCaseExtractStepSingle(t *testing.T) {
// reference extracted variables from previous step // reference extracted variables from previous step
func TestCaseExtractStepAssociation(t *testing.T) { func TestCaseExtractStepAssociation(t *testing.T) {
testcase := &hrp.TestCase{ testcase := &hrp.TestCase{
Config: hrp.TConfig{ Config: hrp.NewConfig("run request with variables").
Name: "run request with variables", SetBaseURL("https://postman-echo.com").
BaseURL: "https://postman-echo.com", SetVerifySSL(false),
Verify: false,
},
TestSteps: []hrp.IStep{ TestSteps: []hrp.IStep{
hrp.Step("get with params"). hrp.NewStep("get with params").
WithVariables(map[string]interface{}{ WithVariables(map[string]interface{}{
"var1": "bar1", "var1": "bar1",
"agent": "HttpRunnerPlus", "agent": "HttpRunnerPlus",
@@ -71,7 +67,7 @@ func TestCaseExtractStepAssociation(t *testing.T) {
AssertEqual("$varFoo1", "bar1", "check args foo1"). AssertEqual("$varFoo1", "bar1", "check args foo1").
AssertEqual("body.args.foo2", "bar2", "check args foo2"). AssertEqual("body.args.foo2", "bar2", "check args foo2").
AssertEqual("body.headers.\"user-agent\"", "HttpRunnerPlus", "check header user agent"), AssertEqual("body.headers.\"user-agent\"", "HttpRunnerPlus", "check header user agent"),
hrp.Step("post json data"). hrp.NewStep("post json data").
POST("/post"). POST("/post").
WithHeaders(map[string]string{"User-Agent": "HttpRunnerPlus"}). WithHeaders(map[string]string{"User-Agent": "HttpRunnerPlus"}).
WithBody(map[string]interface{}{"foo1": "bar1", "foo2": "bar2"}). WithBody(map[string]interface{}{"foo1": "bar1", "foo2": "bar2"}).
+7 -9
View File
@@ -8,18 +8,16 @@ import (
func TestCaseCallFunction(t *testing.T) { func TestCaseCallFunction(t *testing.T) {
testcase := &hrp.TestCase{ testcase := &hrp.TestCase{
Config: hrp.TConfig{ Config: hrp.NewConfig("run request with functions").
Name: "run request with functions", SetBaseURL("https://postman-echo.com").
BaseURL: "https://postman-echo.com", WithVariables(map[string]interface{}{
Verify: false,
Variables: map[string]interface{}{
"n": 5, "n": 5,
"a": 12.3, "a": 12.3,
"b": 3.45, "b": 3.45,
}, }).
}, SetVerifySSL(false),
TestSteps: []hrp.IStep{ TestSteps: []hrp.IStep{
hrp.Step("get with params"). hrp.NewStep("get with params").
GET("/get"). 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)}"}).
WithHeaders(map[string]string{"User-Agent": "HttpRunnerPlus"}). WithHeaders(map[string]string{"User-Agent": "HttpRunnerPlus"}).
@@ -29,7 +27,7 @@ func TestCaseCallFunction(t *testing.T) {
AssertEqual("status_code", 200, "check status code"). AssertEqual("status_code", 200, "check status code").
AssertLengthEqual("body.args.foo1", 5, "check args foo1"). 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"), // notice: request params value will be converted to string
hrp.Step("post json data with functions"). hrp.NewStep("post json data with functions").
POST("/post"). POST("/post").
WithHeaders(map[string]string{"User-Agent": "HttpRunnerPlus"}). WithHeaders(map[string]string{"User-Agent": "HttpRunnerPlus"}).
WithBody(map[string]interface{}{"foo1": "${gen_random_string($n)}", "foo2": "${max($a, $b)}"}). WithBody(map[string]interface{}{"foo1": "${gen_random_string($n)}", "foo2": "${max($a, $b)}"}).
+8 -10
View File
@@ -8,13 +8,11 @@ import (
func TestCaseBasicRequest(t *testing.T) { func TestCaseBasicRequest(t *testing.T) {
testcase := &hrp.TestCase{ testcase := &hrp.TestCase{
Config: hrp.TConfig{ Config: hrp.NewConfig("request methods testcase in hardcode").
Name: "request methods testcase in hardcode", SetBaseURL("https://postman-echo.com").
BaseURL: "https://postman-echo.com", SetVerifySSL(false),
Verify: false,
},
TestSteps: []hrp.IStep{ TestSteps: []hrp.IStep{
hrp.Step("get with params"). hrp.NewStep("get with params").
GET("/get"). GET("/get").
WithParams(map[string]interface{}{"foo1": "bar1", "foo2": "bar2"}). WithParams(map[string]interface{}{"foo1": "bar1", "foo2": "bar2"}).
WithHeaders(map[string]string{ WithHeaders(map[string]string{
@@ -26,7 +24,7 @@ func TestCaseBasicRequest(t *testing.T) {
AssertEqual("headers.\"Content-Type\"", "application/json; charset=utf-8", "check header Content-Type"). AssertEqual("headers.\"Content-Type\"", "application/json; charset=utf-8", "check header Content-Type").
AssertEqual("body.args.foo1", "bar1", "check args foo1"). AssertEqual("body.args.foo1", "bar1", "check args foo1").
AssertEqual("body.args.foo2", "bar2", "check args foo2"), AssertEqual("body.args.foo2", "bar2", "check args foo2"),
hrp.Step("post raw text"). hrp.NewStep("post raw text").
POST("/post"). POST("/post").
WithHeaders(map[string]string{ WithHeaders(map[string]string{
"User-Agent": "HttpRunnerPlus", "User-Agent": "HttpRunnerPlus",
@@ -36,7 +34,7 @@ func TestCaseBasicRequest(t *testing.T) {
Validate(). Validate().
AssertEqual("status_code", 200, "check status code"). AssertEqual("status_code", 200, "check status code").
AssertEqual("body.data", "This is expected to be sent back as part of response body.", "check data"), AssertEqual("body.data", "This is expected to be sent back as part of response body.", "check data"),
hrp.Step("post form data"). hrp.NewStep("post form data").
POST("/post"). POST("/post").
WithHeaders(map[string]string{ WithHeaders(map[string]string{
"User-Agent": "HttpRunnerPlus", "User-Agent": "HttpRunnerPlus",
@@ -47,7 +45,7 @@ func TestCaseBasicRequest(t *testing.T) {
AssertEqual("status_code", 200, "check status code"). AssertEqual("status_code", 200, "check status code").
AssertEqual("body.form.foo1", "bar1", "check form foo1"). AssertEqual("body.form.foo1", "bar1", "check form foo1").
AssertEqual("body.form.foo2", "bar2", "check form foo2"), AssertEqual("body.form.foo2", "bar2", "check form foo2"),
hrp.Step("post json data"). hrp.NewStep("post json data").
POST("/post"). POST("/post").
WithHeaders(map[string]string{ WithHeaders(map[string]string{
"User-Agent": "HttpRunnerPlus", "User-Agent": "HttpRunnerPlus",
@@ -57,7 +55,7 @@ func TestCaseBasicRequest(t *testing.T) {
AssertEqual("status_code", 200, "check status code"). AssertEqual("status_code", 200, "check status code").
AssertEqual("body.json.foo1", "bar1", "check json foo1"). AssertEqual("body.json.foo1", "bar1", "check json foo1").
AssertEqual("body.json.foo2", "bar2", "check json foo2"), AssertEqual("body.json.foo2", "bar2", "check json foo2"),
hrp.Step("put request"). hrp.NewStep("put request").
PUT("/put"). PUT("/put").
WithHeaders(map[string]string{ WithHeaders(map[string]string{
"User-Agent": "HttpRunnerPlus", "User-Agent": "HttpRunnerPlus",
+5 -7
View File
@@ -8,13 +8,11 @@ import (
func TestCaseValidateStep(t *testing.T) { func TestCaseValidateStep(t *testing.T) {
testcase := &hrp.TestCase{ testcase := &hrp.TestCase{
Config: hrp.TConfig{ Config: hrp.NewConfig("run request with validation").
Name: "run request with validation", SetBaseURL("https://postman-echo.com").
BaseURL: "https://postman-echo.com", SetVerifySSL(false),
Verify: false,
},
TestSteps: []hrp.IStep{ TestSteps: []hrp.IStep{
hrp.Step("get with params"). hrp.NewStep("get with params").
WithVariables(map[string]interface{}{ WithVariables(map[string]interface{}{
"var1": "bar1", "var1": "bar1",
"agent": "HttpRunnerPlus", "agent": "HttpRunnerPlus",
@@ -32,7 +30,7 @@ func TestCaseValidateStep(t *testing.T) {
AssertEqual("body.args.foo1", "bar1", "check args foo1"). // assert response json body with jmespath AssertEqual("body.args.foo1", "bar1", "check args foo1"). // assert response json body with jmespath
AssertEqual("body.args.foo2", "bar2", "check args foo2"). AssertEqual("body.args.foo2", "bar2", "check args foo2").
AssertEqual("body.headers.\"user-agent\"", "HttpRunnerPlus", "check header user agent"), AssertEqual("body.headers.\"user-agent\"", "HttpRunnerPlus", "check header user agent"),
hrp.Step("get with params"). hrp.NewStep("get with params").
WithVariables(map[string]interface{}{ WithVariables(map[string]interface{}{
"var1": "bar1", "var1": "bar1",
"agent": "HttpRunnerPlus", "agent": "HttpRunnerPlus",
+20 -31
View File
@@ -8,18 +8,15 @@ import (
func TestCaseConfigVariables(t *testing.T) { func TestCaseConfigVariables(t *testing.T) {
testcase := &hrp.TestCase{ testcase := &hrp.TestCase{
Config: hrp.TConfig{ Config: hrp.NewConfig("run request with variables").
Name: "run request with variables", SetBaseURL("https://postman-echo.com").
BaseURL: "https://postman-echo.com", WithVariables(map[string]interface{}{
Variables: map[string]interface{}{
"var1": "bar1", "var1": "bar1",
"agent": "HttpRunnerPlus", "agent": "HttpRunnerPlus",
"expectedStatusCode": 200, "expectedStatusCode": 200,
}, }).SetVerifySSL(false),
Verify: false,
},
TestSteps: []hrp.IStep{ TestSteps: []hrp.IStep{
hrp.Step("get with params"). hrp.NewStep("get with params").
GET("/get"). GET("/get").
WithParams(map[string]interface{}{"foo1": "$var1", "foo2": "bar2"}). WithParams(map[string]interface{}{"foo1": "$var1", "foo2": "bar2"}).
WithHeaders(map[string]string{"User-Agent": "$agent"}). WithHeaders(map[string]string{"User-Agent": "$agent"}).
@@ -41,13 +38,11 @@ func TestCaseConfigVariables(t *testing.T) {
func TestCaseStepVariables(t *testing.T) { func TestCaseStepVariables(t *testing.T) {
testcase := &hrp.TestCase{ testcase := &hrp.TestCase{
Config: hrp.TConfig{ Config: hrp.NewConfig("run request with variables").
Name: "run request with variables", SetBaseURL("https://postman-echo.com").
BaseURL: "https://postman-echo.com", SetVerifySSL(false),
Verify: false,
},
TestSteps: []hrp.IStep{ TestSteps: []hrp.IStep{
hrp.Step("get with params"). hrp.NewStep("get with params").
WithVariables(map[string]interface{}{ WithVariables(map[string]interface{}{
"var1": "bar1", "var1": "bar1",
"agent": "HttpRunnerPlus", "agent": "HttpRunnerPlus",
@@ -74,18 +69,15 @@ func TestCaseStepVariables(t *testing.T) {
func TestCaseOverrideConfigVariables(t *testing.T) { func TestCaseOverrideConfigVariables(t *testing.T) {
testcase := &hrp.TestCase{ testcase := &hrp.TestCase{
Config: hrp.TConfig{ Config: hrp.NewConfig("run request with variables").
Name: "run request with variables", SetBaseURL("https://postman-echo.com").
BaseURL: "https://postman-echo.com", WithVariables(map[string]interface{}{
Variables: map[string]interface{}{
"var1": "bar0", "var1": "bar0",
"agent": "HttpRunnerPlus", "agent": "HttpRunnerPlus",
"expectedStatusCode": 200, "expectedStatusCode": 200,
}, }).SetVerifySSL(false),
Verify: false,
},
TestSteps: []hrp.IStep{ TestSteps: []hrp.IStep{
hrp.Step("get with params"). hrp.NewStep("get with params").
WithVariables(map[string]interface{}{ WithVariables(map[string]interface{}{
"var1": "bar1", // override config variable "var1": "bar1", // override config variable
"agent": "$agent", // reference config variable "agent": "$agent", // reference config variable
@@ -112,20 +104,17 @@ func TestCaseOverrideConfigVariables(t *testing.T) {
func TestCaseParseVariables(t *testing.T) { func TestCaseParseVariables(t *testing.T) {
testcase := &hrp.TestCase{ testcase := &hrp.TestCase{
Config: hrp.TConfig{ Config: hrp.NewConfig("run request with functions").
Name: "run request with functions", SetBaseURL("https://postman-echo.com").
BaseURL: "https://postman-echo.com", WithVariables(map[string]interface{}{
Verify: false,
Variables: map[string]interface{}{
"n": 5, "n": 5,
"a": 12.3, "a": 12.3,
"b": 3.45, "b": 3.45,
"varFoo1": "${gen_random_string($n)}", "varFoo1": "${gen_random_string($n)}",
"varFoo2": "${max($a, $b)}", // 12.3 "varFoo2": "${max($a, $b)}", // 12.3
}, }).SetVerifySSL(false),
},
TestSteps: []hrp.IStep{ TestSteps: []hrp.IStep{
hrp.Step("get with params"). hrp.NewStep("get with params").
WithVariables(map[string]interface{}{ WithVariables(map[string]interface{}{
"n": 3, "n": 3,
"b": 34.5, "b": 34.5,
@@ -140,7 +129,7 @@ func TestCaseParseVariables(t *testing.T) {
AssertEqual("status_code", 200, "check status code"). AssertEqual("status_code", 200, "check status code").
AssertLengthEqual("body.args.foo1", 5, "check args foo1"). AssertLengthEqual("body.args.foo1", 5, "check args foo1").
AssertEqual("body.args.foo2", "34.5", "check args foo2"), // notice: request params value will be converted to string AssertEqual("body.args.foo2", "34.5", "check args foo2"), // notice: request params value will be converted to string
hrp.Step("post json data with functions"). hrp.NewStep("post json data with functions").
POST("/post"). POST("/post").
WithHeaders(map[string]string{"User-Agent": "HttpRunnerPlus"}). WithHeaders(map[string]string{"User-Agent": "HttpRunnerPlus"}).
WithBody(map[string]interface{}{"foo1": "${gen_random_string($n)}", "foo2": "${max($a, $b)}"}). WithBody(map[string]interface{}{"foo1": "${gen_random_string($n)}", "foo2": "${max($a, $b)}"}).
+5 -3
View File
@@ -7,25 +7,27 @@ type stepRequestExtraction struct {
step *TStep step *TStep
} }
// WithJmesPath sets the JMESPath expression to extract from the response.
func (s *stepRequestExtraction) WithJmesPath(jmesPath string, varName string) *stepRequestExtraction { func (s *stepRequestExtraction) WithJmesPath(jmesPath string, varName string) *stepRequestExtraction {
s.step.Extract[varName] = jmesPath s.step.Extract[varName] = jmesPath
return s return s
} }
// Validate switches to step validation.
func (s *stepRequestExtraction) Validate() *stepRequestValidation { func (s *stepRequestExtraction) Validate() *stepRequestValidation {
return &stepRequestValidation{ return &stepRequestValidation{
step: s.step, step: s.step,
} }
} }
func (s *stepRequestExtraction) Name() string { func (s *stepRequestExtraction) name() string {
return s.step.Name return s.step.Name
} }
func (s *stepRequestExtraction) Type() string { func (s *stepRequestExtraction) getType() string {
return fmt.Sprintf("request-%v", s.step.Request.Method) return fmt.Sprintf("request-%v", s.step.Request.Method)
} }
func (s *stepRequestExtraction) ToStruct() *TStep { func (s *stepRequestExtraction) toStruct() *TStep {
return s.step return s.step
} }
+9 -12
View File
@@ -86,7 +86,7 @@ func (h *HAR) makeTestCase() (*hrp.TCase, error) {
} }
tCase := &hrp.TCase{ tCase := &hrp.TCase{
Config: *h.prepareConfig(), Config: h.prepareConfig(),
TestSteps: teststeps, TestSteps: teststeps,
} }
return tCase, nil return tCase, nil
@@ -114,11 +114,8 @@ func (h *HAR) load() (*Har, error) {
} }
func (h *HAR) prepareConfig() *hrp.TConfig { func (h *HAR) prepareConfig() *hrp.TConfig {
return &hrp.TConfig{ return hrp.NewConfig("testcase description").
Name: "testcase description", SetVerifySSL(false)
Variables: make(map[string]interface{}),
Verify: false,
}
} }
func (h *HAR) prepareTestSteps() ([]*hrp.TStep, error) { func (h *HAR) prepareTestSteps() ([]*hrp.TStep, error) {
@@ -147,8 +144,8 @@ func (h *HAR) prepareTestStep(entry *Entry) (*hrp.TStep, error) {
tStep := &TStep{ tStep := &TStep{
TStep: hrp.TStep{ TStep: hrp.TStep{
Request: &hrp.TRequest{}, Request: &hrp.Request{},
Validators: make([]hrp.TValidator, 0), Validators: make([]hrp.Validator, 0),
}, },
} }
if err := tStep.makeRequestMethod(entry); err != nil { if err := tStep.makeRequestMethod(entry); err != nil {
@@ -180,7 +177,7 @@ type TStep struct {
} }
func (s *TStep) makeRequestMethod(entry *Entry) error { func (s *TStep) makeRequestMethod(entry *Entry) error {
s.Request.Method = hrp.EnumHTTPMethod(entry.Request.Method) s.Request.Method = entry.Request.Method
return nil return nil
} }
@@ -258,7 +255,7 @@ func (s *TStep) makeRequestBody(entry *Entry) error {
func (s *TStep) makeValidate(entry *Entry) error { func (s *TStep) makeValidate(entry *Entry) error {
// make validator for response status code // make validator for response status code
s.Validators = append(s.Validators, hrp.TValidator{ s.Validators = append(s.Validators, hrp.Validator{
Check: "status_code", Check: "status_code",
Assert: "equals", Assert: "equals",
Expect: entry.Response.Status, Expect: entry.Response.Status,
@@ -269,7 +266,7 @@ func (s *TStep) makeValidate(entry *Entry) error {
for _, header := range entry.Response.Headers { for _, header := range entry.Response.Headers {
// assert Content-Type // assert Content-Type
if strings.EqualFold(header.Name, "Content-Type") { if strings.EqualFold(header.Name, "Content-Type") {
s.Validators = append(s.Validators, hrp.TValidator{ s.Validators = append(s.Validators, hrp.Validator{
Check: "headers.Content-Type", Check: "headers.Content-Type",
Assert: "equals", Assert: "equals",
Expect: header.Value, Expect: header.Value,
@@ -318,7 +315,7 @@ func (s *TStep) makeValidate(entry *Entry) error {
case []interface{}: case []interface{}:
continue continue
default: default:
s.Validators = append(s.Validators, hrp.TValidator{ s.Validators = append(s.Validators, hrp.Validator{
Check: fmt.Sprintf("body.%s", key), Check: fmt.Sprintf("body.%s", key),
Assert: "equals", Assert: "equals",
Expect: v, Expect: v,
+1 -1
View File
@@ -1,3 +1,3 @@
package version package version
const VERSION = "v0.2.1" const VERSION = "v0.2.2"
+42 -35
View File
@@ -1,19 +1,19 @@
package hrp package hrp
type EnumHTTPMethod string
const ( const (
GET EnumHTTPMethod = "GET" httpGET string = "GET"
HEAD EnumHTTPMethod = "HEAD" httpHEAD string = "HEAD"
POST EnumHTTPMethod = "POST" httpPOST string = "POST"
PUT EnumHTTPMethod = "PUT" httpPUT string = "PUT"
DELETE EnumHTTPMethod = "DELETE" httpDELETE string = "DELETE"
OPTIONS EnumHTTPMethod = "OPTIONS" httpOPTIONS string = "OPTIONS"
PATCH EnumHTTPMethod = "PATCH" httpPATCH string = "PATCH"
) )
// TConfig represents config data structure for testcase.
// Each testcase should contain one config part.
type TConfig struct { type TConfig struct {
Name string `json:"name" yaml:"name"` Name string `json:"name" yaml:"name"` // required
Verify bool `json:"verify,omitempty" yaml:"verify,omitempty"` Verify bool `json:"verify,omitempty" yaml:"verify,omitempty"`
BaseURL string `json:"base_url,omitempty" yaml:"base_url,omitempty"` BaseURL string `json:"base_url,omitempty" yaml:"base_url,omitempty"`
Variables map[string]interface{} `json:"variables,omitempty" yaml:"variables,omitempty"` Variables map[string]interface{} `json:"variables,omitempty" yaml:"variables,omitempty"`
@@ -22,9 +22,11 @@ type TConfig struct {
Weight int `json:"weight,omitempty" yaml:"weight,omitempty"` Weight int `json:"weight,omitempty" yaml:"weight,omitempty"`
} }
type TRequest struct { // Request represents HTTP request data structure.
Method EnumHTTPMethod `json:"method" yaml:"method"` // This is used for teststep.
URL string `json:"url" yaml:"url"` type Request struct {
Method string `json:"method" yaml:"method"` // required
URL string `json:"url" yaml:"url"` // required
Params map[string]interface{} `json:"params,omitempty" yaml:"params,omitempty"` Params map[string]interface{} `json:"params,omitempty" yaml:"params,omitempty"`
Headers map[string]string `json:"headers,omitempty" yaml:"headers,omitempty"` Headers map[string]string `json:"headers,omitempty" yaml:"headers,omitempty"`
Cookies map[string]string `json:"cookies,omitempty" yaml:"cookies,omitempty"` Cookies map[string]string `json:"cookies,omitempty" yaml:"cookies,omitempty"`
@@ -34,47 +36,52 @@ type TRequest struct {
Verify bool `json:"verify,omitempty" yaml:"verify,omitempty"` Verify bool `json:"verify,omitempty" yaml:"verify,omitempty"`
} }
type TValidator struct { // Validator represents validator for one HTTP response.
Check string `json:"check,omitempty" yaml:"check,omitempty"` // get value with jmespath type Validator struct {
Assert string `json:"assert,omitempty" yaml:"assert,omitempty"` Check string `json:"check" yaml:"check"` // get value with jmespath
Expect interface{} `json:"expect,omitempty" yaml:"expect,omitempty"` Assert string `json:"assert" yaml:"assert"`
Message string `json:"msg,omitempty" yaml:"msg,omitempty"` Expect interface{} `json:"expect" yaml:"expect"`
Message string `json:"msg,omitempty" yaml:"msg,omitempty"` // optional
} }
// TStep represents teststep data structure.
// Each step maybe two different type: make one HTTP request or reference another testcase.
type TStep struct { type TStep struct {
Name string `json:"name" yaml:"name"` Name string `json:"name" yaml:"name"` // required
Transaction string `json:"transaction,omitempty" yaml:"transaction,omitempty"` Request *Request `json:"request,omitempty" yaml:"request,omitempty"`
Request *TRequest `json:"request,omitempty" yaml:"request,omitempty"`
TestCase *TestCase `json:"testcase,omitempty" yaml:"testcase,omitempty"` TestCase *TestCase `json:"testcase,omitempty" yaml:"testcase,omitempty"`
Variables map[string]interface{} `json:"variables,omitempty" yaml:"variables,omitempty"` Variables map[string]interface{} `json:"variables,omitempty" yaml:"variables,omitempty"`
SetupHooks []string `json:"setup_hooks,omitempty" yaml:"setup_hooks,omitempty"` SetupHooks []string `json:"setup_hooks,omitempty" yaml:"setup_hooks,omitempty"`
TeardownHooks []string `json:"teardown_hooks,omitempty" yaml:"teardown_hooks,omitempty"` TeardownHooks []string `json:"teardown_hooks,omitempty" yaml:"teardown_hooks,omitempty"`
Extract map[string]string `json:"extract,omitempty" yaml:"extract,omitempty"` Extract map[string]string `json:"extract,omitempty" yaml:"extract,omitempty"`
Validators []TValidator `json:"validate,omitempty" yaml:"validate,omitempty"` Validators []Validator `json:"validate,omitempty" yaml:"validate,omitempty"`
Export []string `json:"export,omitempty" yaml:"export,omitempty"` Export []string `json:"export,omitempty" yaml:"export,omitempty"`
} }
// used for testcase json loading and dumping // TCase represents testcase data structure.
// Each testcase includes one public config and several sequential teststeps.
type TCase struct { type TCase struct {
Config TConfig `json:"config" yaml:"config"` Config *TConfig `json:"config" yaml:"config"`
TestSteps []*TStep `json:"teststeps" yaml:"teststeps"` TestSteps []*TStep `json:"teststeps" yaml:"teststeps"`
} }
// interface for all types of steps // IStep represents interface for all types for teststeps
type IStep interface { type IStep interface {
Name() string name() string
Type() string getType() string
ToStruct() *TStep toStruct() *TStep
} }
// ITestCase represents interface for all types for testcases
type ITestCase interface { type ITestCase interface {
ToTestCase() (*TestCase, error) ToTestCase() (*TestCase, error)
ToTCase() (*TCase, error) ToTCase() (*TCase, error)
} }
// TestCase is a container for one testcase.
// used for testcase runner // used for testcase runner
type TestCase struct { type TestCase struct {
Config TConfig Config *TConfig
TestSteps []IStep TestSteps []IStep
} }
@@ -86,11 +93,11 @@ type TestCasePath struct {
Path string Path string
} }
type TestCaseSummary struct{} type testCaseSummary struct{}
type StepData struct { type stepData struct {
Name string // step name name string // step name
Success bool // step execution result success bool // step execution result
ResponseLength int64 // response body length responseLength int64 // response body length
ExportVars map[string]interface{} // extract variables exportVars map[string]interface{} // extract variables
} }
+6 -6
View File
@@ -12,7 +12,7 @@ import (
"github.com/httprunner/hrp/internal/builtin" "github.com/httprunner/hrp/internal/builtin"
) )
func NewResponseObject(t *testing.T, resp *http.Response) (*ResponseObject, error) { func newResponseObject(t *testing.T, resp *http.Response) (*responseObject, error) {
// prepare response headers // prepare response headers
headers := make(map[string]string) headers := make(map[string]string)
for k, v := range resp.Header { for k, v := range resp.Header {
@@ -58,7 +58,7 @@ func NewResponseObject(t *testing.T, resp *http.Response) (*ResponseObject, erro
return nil, err return nil, err
} }
return &ResponseObject{ return &responseObject{
t: t, t: t,
respObjMeta: data, respObjMeta: data,
}, nil }, nil
@@ -71,13 +71,13 @@ type respObjMeta struct {
Body interface{} `json:"body"` Body interface{} `json:"body"`
} }
type ResponseObject struct { type responseObject struct {
t *testing.T t *testing.T
respObjMeta interface{} respObjMeta interface{}
validationResults map[string]interface{} validationResults map[string]interface{}
} }
func (v *ResponseObject) Extract(extractors map[string]string) map[string]interface{} { func (v *responseObject) Extract(extractors map[string]string) map[string]interface{} {
if extractors == nil { if extractors == nil {
return nil return nil
} }
@@ -93,7 +93,7 @@ func (v *ResponseObject) Extract(extractors map[string]string) map[string]interf
return extractMapping return extractMapping
} }
func (v *ResponseObject) Validate(validators []TValidator, variablesMapping map[string]interface{}) (err error) { func (v *responseObject) Validate(validators []Validator, variablesMapping map[string]interface{}) (err error) {
for _, validator := range validators { for _, validator := range validators {
// parse check value // parse check value
checkItem := validator.Check checkItem := validator.Check
@@ -133,7 +133,7 @@ func (v *ResponseObject) Validate(validators []TValidator, variablesMapping map[
return nil return nil
} }
func (v *ResponseObject) searchJmespath(expr string) interface{} { func (v *responseObject) searchJmespath(expr string) interface{} {
checkValue, err := jmespath.Search(expr, v.respObjMeta) checkValue, err := jmespath.Search(expr, v.respObjMeta)
if err != nil { if err != nil {
log.Error().Str("expr", expr).Err(err).Msg("search jmespath failed") log.Error().Str("expr", expr).Err(err).Msg("search jmespath failed")
+38 -34
View File
@@ -20,17 +20,18 @@ import (
"github.com/httprunner/hrp/internal/ga" "github.com/httprunner/hrp/internal/ga"
) )
// run API test with default configs // Run starts to run API test with default configs.
func Run(testcases ...ITestCase) error { func Run(testcases ...ITestCase) error {
t := &testing.T{} t := &testing.T{}
return NewRunner(t).SetDebug(true).Run(testcases...) return NewRunner(t).SetDebug(true).Run(testcases...)
} }
func NewRunner(t *testing.T) *Runner { // NewRunner constructs a new runner instance.
func NewRunner(t *testing.T) *runner {
if t == nil { if t == nil {
t = &testing.T{} t = &testing.T{}
} }
return &Runner{ return &runner{
t: t, t: t,
debug: false, // default to turn off debug debug: false, // default to turn off debug
client: &http.Client{ client: &http.Client{
@@ -43,20 +44,22 @@ func NewRunner(t *testing.T) *Runner {
} }
} }
type Runner struct { type runner struct {
t *testing.T t *testing.T
debug bool debug bool
client *http.Client client *http.Client
sessionVariables map[string]interface{} sessionVariables map[string]interface{}
} }
func (r *Runner) SetDebug(debug bool) *Runner { // SetDebug configures whether to log HTTP request and response content.
func (r *runner) SetDebug(debug bool) *runner {
log.Info().Bool("debug", debug).Msg("[init] SetDebug") log.Info().Bool("debug", debug).Msg("[init] SetDebug")
r.debug = debug r.debug = debug
return r return r
} }
func (r *Runner) SetProxyUrl(proxyUrl string) *Runner { // SetProxyUrl configures the proxy URL, which is usually used to capture HTTP packets for debugging.
func (r *runner) SetProxyUrl(proxyUrl string) *runner {
log.Info().Str("proxyUrl", proxyUrl).Msg("[init] SetProxyUrl") log.Info().Str("proxyUrl", proxyUrl).Msg("[init] SetProxyUrl")
p, err := url.Parse(proxyUrl) p, err := url.Parse(proxyUrl)
if err != nil { if err != nil {
@@ -70,7 +73,8 @@ func (r *Runner) SetProxyUrl(proxyUrl string) *Runner {
return r return r
} }
func (r *Runner) Run(testcases ...ITestCase) error { // Run starts to execute one or multiple testcases.
func (r *runner) Run(testcases ...ITestCase) error {
event := ga.EventTracking{ event := ga.EventTracking{
Category: "RunAPITests", Category: "RunAPITests",
Action: "hrp run", Action: "hrp run",
@@ -94,8 +98,8 @@ func (r *Runner) Run(testcases ...ITestCase) error {
return nil return nil
} }
func (r *Runner) runCase(testcase *TestCase) error { func (r *runner) runCase(testcase *TestCase) error {
config := &testcase.Config config := testcase.Config
if err := r.parseConfig(config); err != nil { if err := r.parseConfig(config); err != nil {
return err return err
} }
@@ -113,12 +117,12 @@ func (r *Runner) runCase(testcase *TestCase) error {
return nil return nil
} }
func (r *Runner) runStep(step IStep, config *TConfig) (stepData *StepData, err error) { func (r *runner) runStep(step IStep, config *TConfig) (stepResult *stepData, err error) {
log.Info().Str("step", step.Name()).Msg("run step start") log.Info().Str("step", step.name()).Msg("run step start")
// copy step to avoid data racing // copy step to avoid data racing
copiedStep := &TStep{} copiedStep := &TStep{}
if err = copier.Copy(copiedStep, step.ToStruct()); err != nil { if err = copier.Copy(copiedStep, step.toStruct()); err != nil {
log.Error().Err(err).Msg("copy step data failed") log.Error().Err(err).Msg("copy step data failed")
return return
} }
@@ -142,7 +146,7 @@ func (r *Runner) runStep(step IStep, config *TConfig) (stepData *StepData, err e
// run referenced testcase // run referenced testcase
log.Info().Str("testcase", copiedStep.Name).Msg("run referenced testcase") log.Info().Str("testcase", copiedStep.Name).Msg("run referenced testcase")
// TODO: override testcase config // TODO: override testcase config
stepData, err = r.runStepTestCase(copiedStep) stepResult, err = r.runStepTestCase(copiedStep)
if err != nil { if err != nil {
log.Error().Err(err).Msg("run referenced testcase step failed") log.Error().Err(err).Msg("run referenced testcase step failed")
return return
@@ -150,7 +154,7 @@ func (r *Runner) runStep(step IStep, config *TConfig) (stepData *StepData, err e
} else { } else {
// run request // run request
copiedStep.Request.URL = buildURL(config.BaseURL, copiedStep.Request.URL) // avoid data racing copiedStep.Request.URL = buildURL(config.BaseURL, copiedStep.Request.URL) // avoid data racing
stepData, err = r.runStepRequest(copiedStep) stepResult, err = r.runStepRequest(copiedStep)
if err != nil { if err != nil {
log.Error().Err(err).Msg("run request step failed") log.Error().Err(err).Msg("run request step failed")
return return
@@ -158,23 +162,23 @@ func (r *Runner) runStep(step IStep, config *TConfig) (stepData *StepData, err e
} }
// update extracted variables // update extracted variables
for k, v := range stepData.ExportVars { for k, v := range stepResult.exportVars {
r.sessionVariables[k] = v r.sessionVariables[k] = v
} }
log.Info(). log.Info().
Str("step", step.Name()). Str("step", step.name()).
Bool("success", stepData.Success). Bool("success", stepResult.success).
Interface("exportVars", stepData.ExportVars). Interface("exportVars", stepResult.exportVars).
Msg("run step end") Msg("run step end")
return return
} }
func (r *Runner) runStepRequest(step *TStep) (stepData *StepData, err error) { func (r *runner) runStepRequest(step *TStep) (stepResult *stepData, err error) {
stepData = &StepData{ stepResult = &stepData{
Name: step.Name, name: step.Name,
Success: false, success: false,
ResponseLength: 0, responseLength: 0,
} }
rawUrl := step.Request.URL rawUrl := step.Request.URL
@@ -310,7 +314,7 @@ func (r *Runner) runStepRequest(step *TStep) (stepData *StepData, err error) {
} }
// new response object // new response object
respObj, err := NewResponseObject(r.t, resp) respObj, err := newResponseObject(r.t, resp)
if err != nil { if err != nil {
err = errors.Wrap(err, "init ResponseObject error") err = errors.Wrap(err, "init ResponseObject error")
return return
@@ -319,7 +323,7 @@ func (r *Runner) runStepRequest(step *TStep) (stepData *StepData, err error) {
// extract variables from response // extract variables from response
extractors := step.Extract extractors := step.Extract
extractMapping := respObj.Extract(extractors) extractMapping := respObj.Extract(extractors)
stepData.ExportVars = extractMapping stepResult.exportVars = extractMapping
// override step variables with extracted variables // override step variables with extracted variables
stepVariables := mergeVariables(step.Variables, extractMapping) stepVariables := mergeVariables(step.Variables, extractMapping)
@@ -330,22 +334,22 @@ func (r *Runner) runStepRequest(step *TStep) (stepData *StepData, err error) {
return return
} }
stepData.Success = true stepResult.success = true
stepData.ResponseLength = resp.ContentLength stepResult.responseLength = resp.ContentLength
return return
} }
func (r *Runner) runStepTestCase(step *TStep) (stepData *StepData, err error) { func (r *runner) runStepTestCase(step *TStep) (stepResult *stepData, err error) {
stepData = &StepData{ stepResult = &stepData{
Name: step.Name, name: step.Name,
Success: false, success: false,
} }
testcase := step.TestCase testcase := step.TestCase
err = r.runCase(testcase) err = r.runCase(testcase)
return return
} }
func (r *Runner) parseConfig(config *TConfig) error { func (r *runner) parseConfig(config *TConfig) error {
// parse config variables // parse config variables
parsedVariables, err := parseVariables(config.Variables) parsedVariables, err := parseVariables(config.Variables)
if err != nil { if err != nil {
@@ -371,8 +375,8 @@ func (r *Runner) parseConfig(config *TConfig) error {
return nil return nil
} }
func (r *Runner) GetSummary() *TestCaseSummary { func (r *runner) getSummary() *testCaseSummary {
return &TestCaseSummary{} return &testCaseSummary{}
} }
func setBodyBytes(req *http.Request, data []byte) { func setBodyBytes(req *http.Request, data []byte) {
+6 -11
View File
@@ -6,29 +6,24 @@ import (
func TestHttpRunner(t *testing.T) { func TestHttpRunner(t *testing.T) {
testcase1 := &TestCase{ testcase1 := &TestCase{
Config: TConfig{ Config: NewConfig("TestCase1").
Name: "TestCase1", SetBaseURL("http://httpbin.org"),
BaseURL: "http://httpbin.org",
},
TestSteps: []IStep{ TestSteps: []IStep{
Step("headers"). NewStep("headers").
GET("/headers"). GET("/headers").
Validate(). Validate().
AssertEqual("status_code", 200, "check status code"). AssertEqual("status_code", 200, "check status code").
AssertEqual("headers.\"Content-Type\"", "application/json", "check http response Content-Type"), AssertEqual("headers.\"Content-Type\"", "application/json", "check http response Content-Type"),
Step("user-agent"). NewStep("user-agent").
GET("/user-agent"). GET("/user-agent").
Validate(). Validate().
AssertEqual("status_code", 200, "check status code"). AssertEqual("status_code", 200, "check status code").
AssertEqual("headers.\"Content-Type\"", "application/json", "check http response Content-Type"), AssertEqual("headers.\"Content-Type\"", "application/json", "check http response Content-Type"),
Step("TestCase3").CallRefCase(&TestCase{Config: TConfig{Name: "TestCase3"}}), NewStep("TestCase3").CallRefCase(&TestCase{Config: NewConfig("TestCase3")}),
}, },
} }
testcase2 := &TestCase{ testcase2 := &TestCase{
Config: TConfig{ Config: NewConfig("TestCase2").SetWeight(3),
Name: "TestCase2",
Weight: 3,
},
} }
testcase3 := &TestCasePath{demoTestCaseJSONPath} testcase3 := &TestCasePath{demoTestCaseJSONPath}
+112 -50
View File
@@ -2,104 +2,152 @@ package hrp
import "fmt" import "fmt"
func Step(name string) *step { // NewConfig returns a new constructed testcase config with specified testcase name.
return &step{ func NewConfig(name string) *TConfig {
TStep: &TStep{ return &TConfig{
Name: name, Name: name,
Variables: make(map[string]interface{}), Variables: make(map[string]interface{}),
},
} }
} }
type step struct { // WithVariables sets variables for current testcase.
*TStep func (c *TConfig) WithVariables(variables map[string]interface{}) *TConfig {
c.Variables = variables
return c
} }
func (s *step) WithVariables(variables map[string]interface{}) *step { // SetBaseURL sets base URL for current testcase.
s.TStep.Variables = variables func (c *TConfig) SetBaseURL(baseURL string) *TConfig {
c.BaseURL = baseURL
return c
}
// SetVerifySSL sets whether to verify SSL for current testcase.
func (c *TConfig) SetVerifySSL(verify bool) *TConfig {
c.Verify = verify
return c
}
// WithParameters sets parameters for current testcase.
func (c *TConfig) WithParameters(parameters map[string]interface{}) *TConfig {
c.Parameters = parameters
return c
}
// ExportVars specifies variable names to export for current testcase.
func (c *TConfig) ExportVars(vars ...string) *TConfig {
c.Export = vars
return c
}
// SetWeight sets weight for current testcase, which is used in load testing.
func (c *TConfig) SetWeight(weight int) *TConfig {
c.Weight = weight
return c
}
// NewStep returns a new constructed teststep with specified step name.
func NewStep(name string) *TStep {
return &TStep{
Name: name,
Variables: make(map[string]interface{}),
}
}
// WithVariables sets variables for current teststep.
func (s *TStep) WithVariables(variables map[string]interface{}) *TStep {
s.Variables = variables
return s return s
} }
func (s *step) SetupHook(hook string) *step { // SetupHook adds a setup hook for current teststep.
s.TStep.SetupHooks = append(s.TStep.SetupHooks, hook) func (s *TStep) SetupHook(hook string) *TStep {
s.SetupHooks = append(s.SetupHooks, hook)
return s return s
} }
func (s *step) GET(url string) *requestWithOptionalArgs { // GET makes a HTTP GET request.
s.TStep.Request = &TRequest{ func (s *TStep) GET(url string) *requestWithOptionalArgs {
Method: GET, s.Request = &Request{
Method: httpGET,
URL: url, URL: url,
} }
return &requestWithOptionalArgs{ return &requestWithOptionalArgs{
step: s.TStep, step: s,
} }
} }
func (s *step) HEAD(url string) *requestWithOptionalArgs { // HEAD makes a HTTP HEAD request.
s.TStep.Request = &TRequest{ func (s *TStep) HEAD(url string) *requestWithOptionalArgs {
Method: HEAD, s.Request = &Request{
Method: httpHEAD,
URL: url, URL: url,
} }
return &requestWithOptionalArgs{ return &requestWithOptionalArgs{
step: s.TStep, step: s,
} }
} }
func (s *step) POST(url string) *requestWithOptionalArgs { // POST makes a HTTP POST request.
s.TStep.Request = &TRequest{ func (s *TStep) POST(url string) *requestWithOptionalArgs {
Method: POST, s.Request = &Request{
Method: httpPOST,
URL: url, URL: url,
} }
return &requestWithOptionalArgs{ return &requestWithOptionalArgs{
step: s.TStep, step: s,
} }
} }
func (s *step) PUT(url string) *requestWithOptionalArgs { // PUT makes a HTTP PUT request.
s.TStep.Request = &TRequest{ func (s *TStep) PUT(url string) *requestWithOptionalArgs {
Method: PUT, s.Request = &Request{
Method: httpPUT,
URL: url, URL: url,
} }
return &requestWithOptionalArgs{ return &requestWithOptionalArgs{
step: s.TStep, step: s,
} }
} }
func (s *step) DELETE(url string) *requestWithOptionalArgs { // DELETE makes a HTTP DELETE request.
s.TStep.Request = &TRequest{ func (s *TStep) DELETE(url string) *requestWithOptionalArgs {
Method: DELETE, s.Request = &Request{
Method: httpDELETE,
URL: url, URL: url,
} }
return &requestWithOptionalArgs{ return &requestWithOptionalArgs{
step: s.TStep, step: s,
} }
} }
func (s *step) OPTIONS(url string) *requestWithOptionalArgs { // OPTIONS makes a HTTP OPTIONS request.
s.TStep.Request = &TRequest{ func (s *TStep) OPTIONS(url string) *requestWithOptionalArgs {
Method: OPTIONS, s.Request = &Request{
Method: httpOPTIONS,
URL: url, URL: url,
} }
return &requestWithOptionalArgs{ return &requestWithOptionalArgs{
step: s.TStep, step: s,
} }
} }
func (s *step) PATCH(url string) *requestWithOptionalArgs { // PATCH makes a HTTP PATCH request.
s.TStep.Request = &TRequest{ func (s *TStep) PATCH(url string) *requestWithOptionalArgs {
Method: PATCH, s.Request = &Request{
Method: httpPATCH,
URL: url, URL: url,
} }
return &requestWithOptionalArgs{ return &requestWithOptionalArgs{
step: s.TStep, step: s,
} }
} }
// call referenced testcase // CallRefCase calls a referenced testcase.
func (s *step) CallRefCase(tc *TestCase) *testcaseWithOptionalArgs { func (s *TStep) CallRefCase(tc *TestCase) *testcaseWithOptionalArgs {
s.TStep.TestCase = tc s.TestCase = tc
return &testcaseWithOptionalArgs{ return &testcaseWithOptionalArgs{
step: s.TStep, step: s,
} }
} }
@@ -108,62 +156,74 @@ type requestWithOptionalArgs struct {
step *TStep step *TStep
} }
// SetVerify sets whether to verify SSL for current HTTP request.
func (s *requestWithOptionalArgs) SetVerify(verify bool) *requestWithOptionalArgs { func (s *requestWithOptionalArgs) SetVerify(verify bool) *requestWithOptionalArgs {
s.step.Request.Verify = verify s.step.Request.Verify = verify
return s return s
} }
// SetTimeout sets timeout for current HTTP request.
func (s *requestWithOptionalArgs) SetTimeout(timeout float32) *requestWithOptionalArgs { func (s *requestWithOptionalArgs) SetTimeout(timeout float32) *requestWithOptionalArgs {
s.step.Request.Timeout = timeout s.step.Request.Timeout = timeout
return s return s
} }
// SetProxies sets proxies for current HTTP request.
func (s *requestWithOptionalArgs) SetProxies(proxies map[string]string) *requestWithOptionalArgs { func (s *requestWithOptionalArgs) SetProxies(proxies map[string]string) *requestWithOptionalArgs {
// TODO // TODO
return s return s
} }
// SetAllowRedirects sets whether to allow redirects for current HTTP request.
func (s *requestWithOptionalArgs) SetAllowRedirects(allowRedirects bool) *requestWithOptionalArgs { func (s *requestWithOptionalArgs) SetAllowRedirects(allowRedirects bool) *requestWithOptionalArgs {
s.step.Request.AllowRedirects = allowRedirects s.step.Request.AllowRedirects = allowRedirects
return s return s
} }
// SetAuth sets auth for current HTTP request.
func (s *requestWithOptionalArgs) SetAuth(auth map[string]string) *requestWithOptionalArgs { func (s *requestWithOptionalArgs) SetAuth(auth map[string]string) *requestWithOptionalArgs {
// TODO // TODO
return s return s
} }
// WithParams sets HTTP request params for current step.
func (s *requestWithOptionalArgs) WithParams(params map[string]interface{}) *requestWithOptionalArgs { func (s *requestWithOptionalArgs) WithParams(params map[string]interface{}) *requestWithOptionalArgs {
s.step.Request.Params = params s.step.Request.Params = params
return s return s
} }
// WithHeaders sets HTTP request headers for current step.
func (s *requestWithOptionalArgs) WithHeaders(headers map[string]string) *requestWithOptionalArgs { func (s *requestWithOptionalArgs) WithHeaders(headers map[string]string) *requestWithOptionalArgs {
s.step.Request.Headers = headers s.step.Request.Headers = headers
return s return s
} }
// WithCookies sets HTTP request cookies for current step.
func (s *requestWithOptionalArgs) WithCookies(cookies map[string]string) *requestWithOptionalArgs { func (s *requestWithOptionalArgs) WithCookies(cookies map[string]string) *requestWithOptionalArgs {
s.step.Request.Cookies = cookies s.step.Request.Cookies = cookies
return s return s
} }
// WithBody sets HTTP request body for current step.
func (s *requestWithOptionalArgs) WithBody(body interface{}) *requestWithOptionalArgs { func (s *requestWithOptionalArgs) WithBody(body interface{}) *requestWithOptionalArgs {
s.step.Request.Body = body s.step.Request.Body = body
return s return s
} }
// TeardownHook adds a teardown hook for current teststep.
func (s *requestWithOptionalArgs) TeardownHook(hook string) *requestWithOptionalArgs { func (s *requestWithOptionalArgs) TeardownHook(hook string) *requestWithOptionalArgs {
s.step.TeardownHooks = append(s.step.TeardownHooks, hook) s.step.TeardownHooks = append(s.step.TeardownHooks, hook)
return s return s
} }
// Validate switches to step validation.
func (s *requestWithOptionalArgs) Validate() *stepRequestValidation { func (s *requestWithOptionalArgs) Validate() *stepRequestValidation {
return &stepRequestValidation{ return &stepRequestValidation{
step: s.step, step: s.step,
} }
} }
// Extract switches to step extraction.
func (s *requestWithOptionalArgs) Extract() *stepRequestExtraction { func (s *requestWithOptionalArgs) Extract() *stepRequestExtraction {
s.step.Extract = make(map[string]string) s.step.Extract = make(map[string]string)
return &stepRequestExtraction{ return &stepRequestExtraction{
@@ -171,18 +231,18 @@ func (s *requestWithOptionalArgs) Extract() *stepRequestExtraction {
} }
} }
func (s *requestWithOptionalArgs) Name() string { func (s *requestWithOptionalArgs) name() string {
if s.step.Name != "" { if s.step.Name != "" {
return s.step.Name return s.step.Name
} }
return fmt.Sprintf("%s %s", s.step.Request.Method, s.step.Request.URL) return fmt.Sprintf("%s %s", s.step.Request.Method, s.step.Request.URL)
} }
func (s *requestWithOptionalArgs) Type() string { func (s *requestWithOptionalArgs) getType() string {
return fmt.Sprintf("request-%v", s.step.Request.Method) return fmt.Sprintf("request-%v", s.step.Request.Method)
} }
func (s *requestWithOptionalArgs) ToStruct() *TStep { func (s *requestWithOptionalArgs) toStruct() *TStep {
return s.step return s.step
} }
@@ -191,27 +251,29 @@ type testcaseWithOptionalArgs struct {
step *TStep step *TStep
} }
// TeardownHook adds a teardown hook for current teststep.
func (s *testcaseWithOptionalArgs) TeardownHook(hook string) *testcaseWithOptionalArgs { func (s *testcaseWithOptionalArgs) TeardownHook(hook string) *testcaseWithOptionalArgs {
s.step.TeardownHooks = append(s.step.TeardownHooks, hook) s.step.TeardownHooks = append(s.step.TeardownHooks, hook)
return s return s
} }
// Export specifies variable names to export from referenced testcase for current step.
func (s *testcaseWithOptionalArgs) Export(names ...string) *testcaseWithOptionalArgs { func (s *testcaseWithOptionalArgs) Export(names ...string) *testcaseWithOptionalArgs {
s.step.Export = append(s.step.Export, names...) s.step.Export = append(s.step.Export, names...)
return s return s
} }
func (s *testcaseWithOptionalArgs) Name() string { func (s *testcaseWithOptionalArgs) name() string {
if s.step.Name != "" { if s.step.Name != "" {
return s.step.Name return s.step.Name
} }
return s.step.TestCase.Config.Name return s.step.TestCase.Config.Name
} }
func (s *testcaseWithOptionalArgs) Type() string { func (s *testcaseWithOptionalArgs) getType() string {
return "testcase" return "testcase"
} }
func (s *testcaseWithOptionalArgs) ToStruct() *TStep { func (s *testcaseWithOptionalArgs) toStruct() *TStep {
return s.step return s.step
} }
+5 -7
View File
@@ -5,7 +5,7 @@ import (
) )
var ( var (
stepGET = Step("get with params"). stepGET = NewStep("get with params").
GET("/get"). GET("/get").
WithParams(map[string]interface{}{"foo1": "bar1", "foo2": "bar2"}). WithParams(map[string]interface{}{"foo1": "bar1", "foo2": "bar2"}).
WithHeaders(map[string]string{"User-Agent": "HttpRunnerPlus"}). WithHeaders(map[string]string{"User-Agent": "HttpRunnerPlus"}).
@@ -16,7 +16,7 @@ var (
AssertEqual("headers.\"Content-Type\"", "application/json; charset=utf-8", "check header Content-Type"). AssertEqual("headers.\"Content-Type\"", "application/json; charset=utf-8", "check header Content-Type").
AssertEqual("body.args.foo1", "bar1", "check param foo1"). AssertEqual("body.args.foo1", "bar1", "check param foo1").
AssertEqual("body.args.foo2", "bar2", "check param foo2") AssertEqual("body.args.foo2", "bar2", "check param foo2")
stepPOSTData = Step("post form data"). stepPOSTData = NewStep("post form data").
POST("/post"). POST("/post").
WithParams(map[string]interface{}{"foo1": "bar1", "foo2": "bar2"}). WithParams(map[string]interface{}{"foo1": "bar1", "foo2": "bar2"}).
WithHeaders(map[string]string{"User-Agent": "HttpRunnerPlus", "Content-Type": "application/x-www-form-urlencoded"}). WithHeaders(map[string]string{"User-Agent": "HttpRunnerPlus", "Content-Type": "application/x-www-form-urlencoded"}).
@@ -28,7 +28,7 @@ var (
func TestRunRequestGetToStruct(t *testing.T) { func TestRunRequestGetToStruct(t *testing.T) {
tStep := stepGET.step tStep := stepGET.step
if tStep.Request.Method != GET { if tStep.Request.Method != httpGET {
t.Fatalf("tStep.Request.Method != GET") t.Fatalf("tStep.Request.Method != GET")
} }
if tStep.Request.URL != "/get" { if tStep.Request.URL != "/get" {
@@ -50,7 +50,7 @@ func TestRunRequestGetToStruct(t *testing.T) {
func TestRunRequestPostDataToStruct(t *testing.T) { func TestRunRequestPostDataToStruct(t *testing.T) {
tStep := stepPOSTData.step tStep := stepPOSTData.step
if tStep.Request.Method != POST { if tStep.Request.Method != httpPOST {
t.Fatalf("tStep.Request.Method != POST") t.Fatalf("tStep.Request.Method != POST")
} }
if tStep.Request.URL != "/post" { if tStep.Request.URL != "/post" {
@@ -74,9 +74,7 @@ func TestRunRequestPostDataToStruct(t *testing.T) {
} }
func TestRunRequestRun(t *testing.T) { func TestRunRequestRun(t *testing.T) {
config := &TConfig{ config := NewConfig("test").SetBaseURL("https://postman-echo.com")
BaseURL: "https://postman-echo.com",
}
runner := NewRunner(t).SetDebug(true) runner := NewRunner(t).SetDebug(true)
if _, err := runner.runStep(stepGET, config); err != nil { if _, err := runner.runStep(stepGET, config); err != nil {
t.Fatalf("tStep.Run() error: %s", err) t.Fatalf("tStep.Run() error: %s", err)
+11 -11
View File
@@ -9,61 +9,61 @@ type stepRequestValidation struct {
step *TStep step *TStep
} }
func (s *stepRequestValidation) Name() string { func (s *stepRequestValidation) name() string {
if s.step.Name != "" { if s.step.Name != "" {
return s.step.Name return s.step.Name
} }
return fmt.Sprintf("%s %s", s.step.Request.Method, s.step.Request.URL) return fmt.Sprintf("%s %s", s.step.Request.Method, s.step.Request.URL)
} }
func (s *stepRequestValidation) Type() string { func (s *stepRequestValidation) getType() string {
return fmt.Sprintf("request-%v", s.step.Request.Method) return fmt.Sprintf("request-%v", s.step.Request.Method)
} }
func (s *stepRequestValidation) ToStruct() *TStep { func (s *stepRequestValidation) toStruct() *TStep {
return s.step return s.step
} }
func (s *stepRequestValidation) AssertEqual(jmesPath string, expected interface{}, msg string) *stepRequestValidation { func (s *stepRequestValidation) AssertEqual(jmesPath string, expected interface{}, msg string) *stepRequestValidation {
validator := TValidator{ v := Validator{
Check: jmesPath, Check: jmesPath,
Assert: "equals", Assert: "equals",
Expect: expected, Expect: expected,
Message: msg, Message: msg,
} }
s.step.Validators = append(s.step.Validators, validator) s.step.Validators = append(s.step.Validators, v)
return s return s
} }
func (s *stepRequestValidation) AssertStartsWith(jmesPath string, expected interface{}, msg string) *stepRequestValidation { func (s *stepRequestValidation) AssertStartsWith(jmesPath string, expected interface{}, msg string) *stepRequestValidation {
validator := TValidator{ v := Validator{
Check: jmesPath, Check: jmesPath,
Assert: "startswith", Assert: "startswith",
Expect: expected, Expect: expected,
Message: msg, Message: msg,
} }
s.step.Validators = append(s.step.Validators, validator) s.step.Validators = append(s.step.Validators, v)
return s return s
} }
func (s *stepRequestValidation) AssertEndsWith(jmesPath string, expected interface{}, msg string) *stepRequestValidation { func (s *stepRequestValidation) AssertEndsWith(jmesPath string, expected interface{}, msg string) *stepRequestValidation {
validator := TValidator{ v := Validator{
Check: jmesPath, Check: jmesPath,
Assert: "endswith", Assert: "endswith",
Expect: expected, Expect: expected,
Message: msg, Message: msg,
} }
s.step.Validators = append(s.step.Validators, validator) s.step.Validators = append(s.step.Validators, v)
return s return s
} }
func (s *stepRequestValidation) AssertLengthEqual(jmesPath string, expected interface{}, msg string) *stepRequestValidation { func (s *stepRequestValidation) AssertLengthEqual(jmesPath string, expected interface{}, msg string) *stepRequestValidation {
validator := TValidator{ v := Validator{
Check: jmesPath, Check: jmesPath,
Assert: "length_equals", Assert: "length_equals",
Expect: expected, Expect: expected,
Message: msg, Message: msg,
} }
s.step.Validators = append(s.step.Validators, validator) s.step.Validators = append(s.step.Validators, v)
return s return s
} }