From 2b3c1b3c57cee9d07ba5f9c719536dcb0b44a20f Mon Sep 17 00:00:00 2001 From: debugtalk Date: Tue, 7 Dec 2021 21:16:32 +0800 Subject: [PATCH] change: make API more concise --- README.md | 108 ++++++++++++++++++------------------- boomer.go | 21 ++++---- convert.go | 2 +- examples/postman-echo.json | 4 ++ examples/postman-echo.yaml | 4 ++ extract.go | 6 +-- har2case/core.go | 62 ++++++++++----------- hrp/cmd/boom.go | 2 +- hrp/cmd/har2case.go | 3 +- hrp/cmd/root.go | 7 +-- log.go | 14 +++-- models.go | 10 ++-- runner.go | 30 +++++------ step.go | 12 ++--- validate.go | 6 +-- 15 files changed, 154 insertions(+), 137 deletions(-) diff --git a/README.md b/README.md index 7370fe5..cebfab0 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ Since installed, you will get a `hrp` command with multiple sub-commands. ```text $ hrp -h -hrp (HttpRunner+) is the next generation for HttpRunner. Enjoy! ✨ 🚀 ✨ +hrp (HttpRunner+) is the one-stop solution for HTTP(S) testing. Enjoy! ✨ 🚀 ✨ License: Apache-2.0 Github: https://github.com/httprunner/hrp @@ -65,17 +65,17 @@ You can use `hrp run` command to run HttpRunner JSON/YAML testcases. The followi $ hrp run examples/demo.json ```text -8:04PM INF Set log to pretty console -8:04PM INF Set log level to INFO -8:04PM INF [init] SetDebug debug=true -8:04PM INF load json testcase path=/Users/debugtalk/MyProjects/HttpRunner-dev/hrp/examples/demo.json -8:04PM INF call function success arguments=[5] funcName=gen_random_string output=B64R8 -8:04PM INF call function success arguments=[12.3,3.45] funcName=max output=12.3 -8:04PM INF run testcase start testcase="demo with complex mechanisms" -8:04PM INF call function success arguments=[12.3,34.5] funcName=max output=34.5 -8:04PM INF run step start step="get with params" +9:22PM INF Set log to color console other than JSON format. +9:22PM INF Set log level to INFO +9:22PM INF [init] SetDebug debug=true +9:22PM INF load json testcase path=/Users/debugtalk/MyProjects/HttpRunner-dev/hrp/examples/demo.json +9:22PM INF call function success arguments=[5] funcName=gen_random_string output=rWRNY +9:22PM INF call function success arguments=[12.3,3.45] funcName=max output=12.3 +9:22PM INF run testcase start testcase="demo with complex mechanisms" +9:22PM INF run step start step="get with params" +9:22PM INF call function success arguments=[12.3,34.5] funcName=max output=34.5 -------------------- request -------------------- -GET /get?foo1=B64R8&foo2=34.5 HTTP/1.1 +GET /get?foo1=rWRNY&foo2=34.5 HTTP/1.1 Host: postman-echo.com User-Agent: HttpRunnerPlus @@ -85,70 +85,70 @@ HTTP/1.1 200 OK Content-Length: 304 Connection: keep-alive Content-Type: application/json; charset=utf-8 -Date: Thu, 11 Nov 2021 12:04:32 GMT -Etag: W/"130-LUQ0LVU7KVSZha0O3nQxqPlr5dw" -Set-Cookie: sails.sid=s%3Ag6vZXrHHzs-B7Q1bFrYQq83dUje_EkSu.06vsqbkZvIOJ6mb1It7c6i354e%2B0t91K4cG14YFjSX0; Path=/; HttpOnly +Date: Tue, 07 Dec 2021 13:22:50 GMT +Etag: W/"130-gmtE0VWiyE0mXUGoJe5AyhMQ2ig" +Set-Cookie: sails.sid=s%3AEWPwP8H-nbpSrCseeulwDQ8OEtRy1pGu.aHV6KrEIiFgaJsUAuDmmmJCYiV6XkrHLS%2Fd9g9vtZQw; Path=/; HttpOnly Vary: Accept-Encoding -{"args":{"foo1":"B64R8","foo2":"34.5"},"headers":{"x-forwarded-proto":"https","x-forwarded-port":"443","host":"postman-echo.com","x-amzn-trace-id":"Root=1-618d06d0-7516144f65e561a8238adab5","user-agent":"HttpRunnerPlus","accept-encoding":"gzip"},"url":"https://postman-echo.com/get?foo1=B64R8&foo2=34.5"} +{"args":{"foo1":"rWRNY","foo2":"34.5"},"headers":{"x-forwarded-proto":"https","x-forwarded-port":"443","host":"postman-echo.com","x-amzn-trace-id":"Root=1-61af602a-5eea88ee21122daf4e8dfe95","user-agent":"HttpRunnerPlus","accept-encoding":"gzip"},"url":"https://postman-echo.com/get?foo1=rWRNY&foo2=34.5"} -------------------------------------------------- -8:04PM INF extract value from=body.args.foo1 value=B64R8 -8:04PM INF set variable value=B64R8 variable=varFoo1 -8:04PM INF validate status_code assertMethod=equals checkValue=200 expectValue=200 result=true -8:04PM INF validate headers."Content-Type" assertMethod=startswith checkValue="application/json; charset=utf-8" expectValue=application/json result=true -8:04PM INF validate body.args.foo1 assertMethod=length_equals checkValue=B64R8 expectValue=5 result=true -8:04PM INF validate $varFoo1 assertMethod=length_equals checkValue=B64R8 expectValue=5 result=true -8:04PM INF validate body.args.foo2 assertMethod=equals checkValue=34.5 expectValue=34.5 result=true -8:04PM INF run step end exportVars={"varFoo1":"B64R8"} step="get with params" success=true -8:04PM INF run step start step="post json data" -8:04PM INF call function success arguments=[12.3,3.45] funcName=max output=12.3 +9:22PM INF extract value from=body.args.foo1 value=rWRNY +9:22PM INF set variable value=rWRNY variable=varFoo1 +9:22PM INF validate status_code assertMethod=equals checkValue=200 expectValue=200 result=true +9:22PM INF validate headers."Content-Type" assertMethod=startswith checkValue="application/json; charset=utf-8" expectValue=application/json result=true +9:22PM INF validate body.args.foo1 assertMethod=length_equals checkValue=rWRNY expectValue=5 result=true +9:22PM INF validate $varFoo1 assertMethod=length_equals checkValue=rWRNY expectValue=5 result=true +9:22PM INF validate body.args.foo2 assertMethod=equals checkValue=34.5 expectValue=34.5 result=true +9:22PM INF run step end exportVars={"varFoo1":"rWRNY"} step="get with params" success=true +9:22PM INF run step start step="post json data" +9:22PM INF call function success arguments=[12.3,3.45] funcName=max output=12.3 -------------------- request -------------------- POST /post HTTP/1.1 Host: postman-echo.com Content-Type: application/json; charset=UTF-8 -{"foo1":"B64R8","foo2":12.3} +{"foo1":"rWRNY","foo2":12.3} ==================== response =================== HTTP/1.1 200 OK Content-Length: 424 Connection: keep-alive Content-Type: application/json; charset=utf-8 -Date: Thu, 11 Nov 2021 12:04:32 GMT -Etag: W/"1a8-1umvYElau4WkHR7VON+jKXozT2c" -Set-Cookie: sails.sid=s%3AeNnS5IE6TBePzx95OfuwyIweJy5aExb0.7MH6Vb42vbZ6OhNT2nhQGcAmHgqcFmtM8X03Qsoxa1k; Path=/; HttpOnly +Date: Tue, 07 Dec 2021 13:22:50 GMT +Etag: W/"1a8-5fCAlcltnCS4Ed/6OxpH9i9dlKs" +Set-Cookie: sails.sid=s%3As1b8P7f8sc3JRNumS-XJrzbwb5oxdkOs.pXRRifddVUiWuzAxwBikBxf3ayM8OahgDDzP7kSnMCc; Path=/; HttpOnly Vary: Accept-Encoding -{"args":{},"data":{"foo1":"B64R8","foo2":12.3},"files":{},"form":{},"headers":{"x-forwarded-proto":"https","x-forwarded-port":"443","host":"postman-echo.com","x-amzn-trace-id":"Root=1-618d06d0-360475ad34903a97191978d7","content-length":"28","user-agent":"Go-http-client/1.1","content-type":"application/json; charset=UTF-8","accept-encoding":"gzip"},"json":{"foo1":"B64R8","foo2":12.3},"url":"https://postman-echo.com/post"} +{"args":{},"data":{"foo1":"rWRNY","foo2":12.3},"files":{},"form":{},"headers":{"x-forwarded-proto":"https","x-forwarded-port":"443","host":"postman-echo.com","x-amzn-trace-id":"Root=1-61af602a-54fcb6412d2d064822bcdd5f","content-length":"28","user-agent":"Go-http-client/1.1","content-type":"application/json; charset=UTF-8","accept-encoding":"gzip"},"json":{"foo1":"rWRNY","foo2":12.3},"url":"https://postman-echo.com/post"} -------------------------------------------------- -8:04PM INF validate status_code assertMethod=equals checkValue=200 expectValue=200 result=true -8:04PM INF validate body.json.foo1 assertMethod=length_equals checkValue=B64R8 expectValue=5 result=true -8:04PM INF validate body.json.foo2 assertMethod=equals checkValue=12.3 expectValue=12.3 result=true -8:04PM INF run step end exportVars=null step="post json data" success=true -8:04PM INF run step start step="post form data" -8:04PM INF call function success arguments=[12.3,3.45] funcName=max output=12.3 +9:22PM INF validate status_code assertMethod=equals checkValue=200 expectValue=200 result=true +9:22PM INF validate body.json.foo1 assertMethod=length_equals checkValue=rWRNY expectValue=5 result=true +9:22PM INF validate body.json.foo2 assertMethod=equals checkValue=12.3 expectValue=12.3 result=true +9:22PM INF run step end exportVars=null step="post json data" success=true +9:22PM INF run step start step="post form data" +9:22PM INF call function success arguments=[12.3,3.45] funcName=max output=12.3 -------------------- request -------------------- POST /post HTTP/1.1 Host: postman-echo.com Content-Type: application/x-www-form-urlencoded; charset=UTF-8 -foo1=B64R8&foo2=12.3 +foo1=rWRNY&foo2=12.3 ==================== response =================== HTTP/1.1 200 OK Content-Length: 445 Connection: keep-alive Content-Type: application/json; charset=utf-8 -Date: Thu, 11 Nov 2021 12:04:32 GMT -Etag: W/"1bd-g/z+op+J2/U1DlrEv2g2VhZ0on4" -Set-Cookie: sails.sid=s%3ALfq9XEgKVT4dKQ8PnxUJ9-WSq4wI96Po.2P90TP9V2Pje3GNJ1hJmLcRRgcQy%2FDwBPF63Xdvdq4o; Path=/; HttpOnly +Date: Tue, 07 Dec 2021 13:22:50 GMT +Etag: W/"1bd-V7gWOjKCZvyBWVyqprN77w2dmXE" +Set-Cookie: sails.sid=s%3Aj4sUA8hI4rAt9JMq1m4k_chSDlfkAEBV.ZfisF4bIH2e7iBY6%2BSHqUbHNBbhCzZi%2Fu4byLDdxy%2B4; Path=/; HttpOnly Vary: Accept-Encoding -{"args":{},"data":"","files":{},"form":{"foo1":"B64R8","foo2":"12.3"},"headers":{"x-forwarded-proto":"https","x-forwarded-port":"443","host":"postman-echo.com","x-amzn-trace-id":"Root=1-618d06d0-56d250242bf05b7144edf2cb","content-length":"20","user-agent":"Go-http-client/1.1","content-type":"application/x-www-form-urlencoded; charset=UTF-8","accept-encoding":"gzip"},"json":{"foo1":"B64R8","foo2":"12.3"},"url":"https://postman-echo.com/post"} +{"args":{},"data":"","files":{},"form":{"foo1":"rWRNY","foo2":"12.3"},"headers":{"x-forwarded-proto":"https","x-forwarded-port":"443","host":"postman-echo.com","x-amzn-trace-id":"Root=1-61af602a-2cc056eb54ba2f0c6850d84a","content-length":"20","user-agent":"Go-http-client/1.1","content-type":"application/x-www-form-urlencoded; charset=UTF-8","accept-encoding":"gzip"},"json":{"foo1":"rWRNY","foo2":"12.3"},"url":"https://postman-echo.com/post"} -------------------------------------------------- -8:04PM INF validate status_code assertMethod=equals checkValue=200 expectValue=200 result=true -8:04PM INF validate body.form.foo1 assertMethod=length_equals checkValue=B64R8 expectValue=5 result=true -8:04PM INF validate body.form.foo2 assertMethod=equals checkValue=12.3 expectValue=12.3 result=true -8:04PM INF run step end exportVars=null step="post form data" success=true -8:04PM INF run testcase end testcase="demo with complex mechanisms" +9:22PM INF validate status_code assertMethod=equals checkValue=200 expectValue=200 result=true +9:22PM INF validate body.form.foo1 assertMethod=length_equals checkValue=rWRNY expectValue=5 result=true +9:22PM INF validate body.form.foo2 assertMethod=equals checkValue=12.3 expectValue=12.3 result=true +9:22PM INF run step end exportVars=null step="post form data" success=true +9:22PM INF run testcase end testcase="demo with complex mechanisms" ``` @@ -175,19 +175,17 @@ import ( func TestCaseDemo(t *testing.T) { demoTestCase := &hrp.TestCase{ - Config: hrp.TConfig{ - Name: "demo with complex mechanisms", - BaseURL: "https://postman-echo.com", - Variables: map[string]interface{}{ // global level variables + Config: hrp.NewConfig("demo with complex mechanisms"). + SetBaseURL("https://postman-echo.com"). + WithVariables(map[string]interface{}{ // global level variables "n": 5, "a": 12.3, "b": 3.45, "varFoo1": "${gen_random_string($n)}", "varFoo2": "${max($a, $b)}", // 12.3; eval with built-in function - }, - }, + }), TestSteps: []hrp.IStep{ - hrp.Step("get with params"). + hrp.NewStep("get with params"). WithVariables(map[string]interface{}{ // step level variables "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 @@ -204,7 +202,7 @@ func TestCaseDemo(t *testing.T) { 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 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"). WithBody(map[string]interface{}{ "foo1": "$varFoo1", // reference former extracted variable @@ -214,7 +212,7 @@ func TestCaseDemo(t *testing.T) { AssertEqual("status_code", 200, "check status code"). AssertLengthEqual("body.json.foo1", 5, "check args foo1"). AssertEqual("body.json.foo2", 12.3, "check args foo2"), - hrp.Step("post form data"). + hrp.NewStep("post form data"). POST("/post"). WithHeaders(map[string]string{"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"}). WithBody(map[string]interface{}{ diff --git a/boomer.go b/boomer.go index 22c9a41..4b7da8a 100644 --- a/boomer.go +++ b/boomer.go @@ -8,25 +8,27 @@ import ( "github.com/httprunner/hrp/internal/ga" ) -func NewStandaloneBoomer(spawnCount int, spawnRate float64) *Boomer { - b := &Boomer{ +func NewStandaloneBoomer(spawnCount int, spawnRate float64) *hrpBoomer { + b := &hrpBoomer{ Boomer: boomer.NewStandaloneBoomer(spawnCount, spawnRate), debug: false, } return b } -type Boomer struct { +type hrpBoomer struct { *boomer.Boomer debug bool } -func (b *Boomer) SetDebug(debug bool) *Boomer { +// SetDebug configures whether to log HTTP request and response content. +func (b *hrpBoomer) SetDebug(debug bool) *hrpBoomer { b.debug = debug return b } -func (b *Boomer) Run(testcases ...ITestCase) { +// Run starts to run load test for one or multiple testcases. +func (b *hrpBoomer) Run(testcases ...ITestCase) { event := ga.EventTracking{ Category: "RunLoadTests", Action: "hrp boom", @@ -48,11 +50,12 @@ func (b *Boomer) Run(testcases ...ITestCase) { b.Boomer.Run(taskSlice...) } -func (b *Boomer) Quit() { +// Quit stops running load test. +func (b *hrpBoomer) Quit() { b.Boomer.Quit() } -func (b *Boomer) convertBoomerTask(testcase *TestCase) *boomer.Task { +func (b *hrpBoomer) convertBoomerTask(testcase *TestCase) *boomer.Task { return &boomer.Task{ Name: testcase.Config.Name, Weight: testcase.Config.Weight, @@ -65,9 +68,9 @@ func (b *Boomer) convertBoomerTask(testcase *TestCase) *boomer.Task { stepData, err := runner.runStep(step, config) elapsed := time.Since(start).Nanoseconds() / int64(time.Millisecond) if err == nil { - b.RecordSuccess(step.getType(), step.name(), elapsed, stepData.responseLength) + b.RecordSuccess(step.Type(), step.Name(), elapsed, stepData.responseLength) } else { - b.RecordFailure(step.getType(), step.name(), elapsed, err.Error()) + b.RecordFailure(step.Type(), step.Name(), elapsed, err.Error()) } } }, diff --git a/convert.go b/convert.go index e25f8be..978818c 100644 --- a/convert.go +++ b/convert.go @@ -15,7 +15,7 @@ func (tc *TestCase) ToTCase() (*TCase, error) { Config: tc.Config, } for _, step := range tc.TestSteps { - tCase.TestSteps = append(tCase.TestSteps, step.toStruct()) + tCase.TestSteps = append(tCase.TestSteps, step.ToStruct()) } return &tCase, nil } diff --git a/examples/postman-echo.json b/examples/postman-echo.json index 125a4f0..9c26ec7 100644 --- a/examples/postman-echo.json +++ b/examples/postman-echo.json @@ -89,6 +89,7 @@ { "check": "body.json", "assert": "equals", + "expect": null, "msg": "assert response body json" }, { @@ -190,6 +191,7 @@ { "check": "body.json", "assert": "equals", + "expect": null, "msg": "assert response body json" }, { @@ -243,6 +245,7 @@ { "check": "body.json", "assert": "equals", + "expect": null, "msg": "assert response body json" }, { @@ -296,6 +299,7 @@ { "check": "body.json", "assert": "equals", + "expect": null, "msg": "assert response body json" }, { diff --git a/examples/postman-echo.yaml b/examples/postman-echo.yaml index 4b2e225..78b5e9b 100644 --- a/examples/postman-echo.yaml +++ b/examples/postman-echo.yaml @@ -63,6 +63,7 @@ teststeps: msg: assert response body data - check: body.json assert: equals + expect: null msg: assert response body json - check: body.url assert: equals @@ -134,6 +135,7 @@ teststeps: msg: assert response body data - check: body.json assert: equals + expect: null msg: assert response body json - check: body.url assert: equals @@ -171,6 +173,7 @@ teststeps: msg: assert response body data - check: body.json assert: equals + expect: null msg: assert response body json - check: body.url assert: equals @@ -208,6 +211,7 @@ teststeps: msg: assert response body data - check: body.json assert: equals + expect: null msg: assert response body json - check: body.url assert: equals diff --git a/extract.go b/extract.go index 40080e3..0d0fb38 100644 --- a/extract.go +++ b/extract.go @@ -20,14 +20,14 @@ func (s *stepRequestExtraction) Validate() *stepRequestValidation { } } -func (s *stepRequestExtraction) name() string { +func (s *stepRequestExtraction) Name() string { return s.step.Name } -func (s *stepRequestExtraction) getType() string { +func (s *stepRequestExtraction) Type() string { return fmt.Sprintf("request-%v", s.step.Request.Method) } -func (s *stepRequestExtraction) toStruct() *TStep { +func (s *stepRequestExtraction) ToStruct() *TStep { return s.step } diff --git a/har2case/core.go b/har2case/core.go index 0e48957..5271fc1 100644 --- a/har2case/core.go +++ b/har2case/core.go @@ -12,36 +12,38 @@ import ( "strings" "github.com/pkg/errors" + "github.com/rs/zerolog" "github.com/httprunner/hrp" "github.com/httprunner/hrp/internal/ga" ) -var log = hrp.GetLogger() +var log zerolog.Logger const ( suffixJSON = ".json" suffixYAML = ".yaml" ) -func NewHAR(path string) *HAR { - return &HAR{ +func NewHAR(path string) *har { + log = hrp.GetLogger() + return &har{ path: path, } } -type HAR struct { +type har struct { path string filterStr string excludeStr string outputDir string } -func (h *HAR) SetOutputDir(dir string) { +func (h *har) SetOutputDir(dir string) { h.outputDir = dir } -func (h *HAR) GenJSON() (jsonPath string, err error) { +func (h *har) GenJSON() (jsonPath string, err error) { event := ga.EventTracking{ Category: "har2case", Action: "hrp har2case --to-json", @@ -60,7 +62,7 @@ func (h *HAR) GenJSON() (jsonPath string, err error) { return } -func (h *HAR) GenYAML() (yamlPath string, err error) { +func (h *har) GenYAML() (yamlPath string, err error) { event := ga.EventTracking{ Category: "har2case", Action: "hrp har2case --to-yaml", @@ -79,7 +81,7 @@ func (h *HAR) GenYAML() (yamlPath string, err error) { return } -func (h *HAR) makeTestCase() (*hrp.TCase, error) { +func (h *har) makeTestCase() (*hrp.TCase, error) { teststeps, err := h.prepareTestSteps() if err != nil { return nil, err @@ -92,7 +94,7 @@ func (h *HAR) makeTestCase() (*hrp.TCase, error) { return tCase, nil } -func (h *HAR) load() (*Har, error) { +func (h *har) load() (*Har, error) { fp, err := os.Open(h.path) if err != nil { return nil, fmt.Errorf("open: %w", err) @@ -113,12 +115,12 @@ func (h *HAR) load() (*Har, error) { return har, nil } -func (h *HAR) prepareConfig() *hrp.TConfig { +func (h *har) prepareConfig() *hrp.TConfig { return hrp.NewConfig("testcase description"). SetVerifySSL(false) } -func (h *HAR) prepareTestSteps() ([]*hrp.TStep, error) { +func (h *har) prepareTestSteps() ([]*hrp.TStep, error) { har, err := h.load() if err != nil { return nil, err @@ -136,52 +138,52 @@ func (h *HAR) prepareTestSteps() ([]*hrp.TStep, error) { return steps, nil } -func (h *HAR) prepareTestStep(entry *Entry) (*hrp.TStep, error) { +func (h *har) prepareTestStep(entry *Entry) (*hrp.TStep, error) { log.Info(). Str("method", entry.Request.Method). Str("url", entry.Request.URL). Msg("convert teststep") - tStep := &TStep{ + step := &tStep{ TStep: hrp.TStep{ Request: &hrp.Request{}, Validators: make([]hrp.Validator, 0), }, } - if err := tStep.makeRequestMethod(entry); err != nil { + if err := step.makeRequestMethod(entry); err != nil { return nil, err } - if err := tStep.makeRequestURL(entry); err != nil { + if err := step.makeRequestURL(entry); err != nil { return nil, err } - if err := tStep.makeRequestParams(entry); err != nil { + if err := step.makeRequestParams(entry); err != nil { return nil, err } - if err := tStep.makeRequestCookies(entry); err != nil { + if err := step.makeRequestCookies(entry); err != nil { return nil, err } - if err := tStep.makeRequestHeaders(entry); err != nil { + if err := step.makeRequestHeaders(entry); err != nil { return nil, err } - if err := tStep.makeRequestBody(entry); err != nil { + if err := step.makeRequestBody(entry); err != nil { return nil, err } - if err := tStep.makeValidate(entry); err != nil { + if err := step.makeValidate(entry); err != nil { return nil, err } - return &tStep.TStep, nil + return &step.TStep, nil } -type TStep struct { +type tStep struct { hrp.TStep } -func (s *TStep) makeRequestMethod(entry *Entry) error { +func (s *tStep) makeRequestMethod(entry *Entry) error { s.Request.Method = entry.Request.Method return nil } -func (s *TStep) makeRequestURL(entry *Entry) error { +func (s *tStep) makeRequestURL(entry *Entry) error { u, err := url.Parse(entry.Request.URL) if err != nil { @@ -192,7 +194,7 @@ func (s *TStep) makeRequestURL(entry *Entry) error { return nil } -func (s *TStep) makeRequestParams(entry *Entry) error { +func (s *tStep) makeRequestParams(entry *Entry) error { s.Request.Params = make(map[string]interface{}) for _, param := range entry.Request.QueryString { s.Request.Params[param.Name] = param.Value @@ -200,7 +202,7 @@ func (s *TStep) makeRequestParams(entry *Entry) error { return nil } -func (s *TStep) makeRequestCookies(entry *Entry) error { +func (s *tStep) makeRequestCookies(entry *Entry) error { s.Request.Cookies = make(map[string]string) for _, cookie := range entry.Request.Cookies { s.Request.Cookies[cookie.Name] = cookie.Value @@ -208,7 +210,7 @@ func (s *TStep) makeRequestCookies(entry *Entry) error { return nil } -func (s *TStep) makeRequestHeaders(entry *Entry) error { +func (s *tStep) makeRequestHeaders(entry *Entry) error { s.Request.Headers = make(map[string]string) for _, header := range entry.Request.Headers { if strings.EqualFold(header.Name, "cookie") { @@ -219,7 +221,7 @@ func (s *TStep) makeRequestHeaders(entry *Entry) error { return nil } -func (s *TStep) makeRequestBody(entry *Entry) error { +func (s *tStep) makeRequestBody(entry *Entry) error { mimeType := entry.Request.PostData.MimeType if mimeType == "" { // GET/HEAD/DELETE without body @@ -253,7 +255,7 @@ func (s *TStep) makeRequestBody(entry *Entry) error { return nil } -func (s *TStep) makeValidate(entry *Entry) error { +func (s *tStep) makeValidate(entry *Entry) error { // make validator for response status code s.Validators = append(s.Validators, hrp.Validator{ Check: "status_code", @@ -329,7 +331,7 @@ func (s *TStep) makeValidate(entry *Entry) error { return nil } -func (h *HAR) genOutputPath(suffix string) string { +func (h *har) genOutputPath(suffix string) string { file := getFilenameWithoutExtension(h.path) + suffix if h.outputDir != "" { return filepath.Join(h.outputDir, file) diff --git a/hrp/cmd/boom.go b/hrp/cmd/boom.go index 9354a90..e74d3d5 100644 --- a/hrp/cmd/boom.go +++ b/hrp/cmd/boom.go @@ -19,7 +19,7 @@ var boomCmd = &cobra.Command{ $ hrp boom examples/ # run testcases in specified folder`, Args: cobra.MinimumNArgs(1), PreRun: func(cmd *cobra.Command, args []string) { - hrp.SetLogLevel("WARN") // disable info logs for load testing + hrp.SetLogger("WARN", logJSON) // disable info logs for load testing }, Run: func(cmd *cobra.Command, args []string) { var paths []hrp.ITestCase diff --git a/hrp/cmd/har2case.go b/hrp/cmd/har2case.go index 749ff4e..a3fc47b 100644 --- a/hrp/cmd/har2case.go +++ b/hrp/cmd/har2case.go @@ -1,9 +1,9 @@ package cmd import ( - "github.com/rs/zerolog/log" "github.com/spf13/cobra" + "github.com/httprunner/hrp" "github.com/httprunner/hrp/har2case" ) @@ -37,6 +37,7 @@ var har2caseCmd = &cobra.Command{ } outputFiles = append(outputFiles, outputPath) } + log := hrp.GetLogger() log.Info().Strs("output", outputFiles).Msg("convert testcase success") return nil }, diff --git a/hrp/cmd/root.go b/hrp/cmd/root.go index d73c699..c49caad 100644 --- a/hrp/cmd/root.go +++ b/hrp/cmd/root.go @@ -14,16 +14,13 @@ import ( var RootCmd = &cobra.Command{ Use: "hrp", Short: "One-stop solution for HTTP(S) testing.", - Long: `hrp (HttpRunner+) is the next generation for HttpRunner. Enjoy! ✨ 🚀 ✨ + Long: `hrp (HttpRunner+) is the one-stop solution for HTTP(S) testing. Enjoy! ✨ 🚀 ✨ License: Apache-2.0 Github: https://github.com/httprunner/hrp Copyright 2021 debugtalk`, PersistentPreRun: func(cmd *cobra.Command, args []string) { - if !logJSON { - hrp.SetLogPretty() - } - hrp.SetLogLevel(logLevel) + hrp.SetLogger(logLevel, logJSON) }, Version: version.VERSION, } diff --git a/log.go b/log.go index af5dcf9..cdd3867 100644 --- a/log.go +++ b/log.go @@ -10,7 +10,15 @@ import ( var log = zlog.Logger -func SetLogLevel(level string) { +// SetLogger configures the log level and format. +func SetLogger(level string, logJSON bool) { + if !logJSON { + setLogPretty() + } + setLogLevel(level) +} + +func setLogLevel(level string) { level = strings.ToUpper(level) log.Info().Msgf("Set log level to %s", level) switch level { @@ -29,9 +37,9 @@ func SetLogLevel(level string) { } } -func SetLogPretty() { +func setLogPretty() { log = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}) - log.Info().Msg("Set log to pretty console") + log.Info().Msg("Set log to color console other than JSON format.") } func GetLogger() zerolog.Logger { diff --git a/models.go b/models.go index c1ee30c..20f727e 100644 --- a/models.go +++ b/models.go @@ -65,14 +65,14 @@ type TCase struct { TestSteps []*TStep `json:"teststeps" yaml:"teststeps"` } -// IStep represents interface for all types for teststeps +// IStep represents interface for all types for teststeps. type IStep interface { - name() string - getType() string - toStruct() *TStep + Name() string + Type() string + ToStruct() *TStep } -// ITestCase represents interface for all types for testcases +// ITestCase represents interface for all types for testcases. type ITestCase interface { ToTestCase() (*TestCase, error) ToTCase() (*TCase, error) diff --git a/runner.go b/runner.go index 58eb6c2..0f650f0 100644 --- a/runner.go +++ b/runner.go @@ -27,11 +27,11 @@ func Run(testcases ...ITestCase) error { } // NewRunner constructs a new runner instance. -func NewRunner(t *testing.T) *runner { +func NewRunner(t *testing.T) *hrpRunner { if t == nil { t = &testing.T{} } - return &runner{ + return &hrpRunner{ t: t, debug: false, // default to turn off debug client: &http.Client{ @@ -44,7 +44,7 @@ func NewRunner(t *testing.T) *runner { } } -type runner struct { +type hrpRunner struct { t *testing.T debug bool client *http.Client @@ -52,14 +52,14 @@ type runner struct { } // SetDebug configures whether to log HTTP request and response content. -func (r *runner) SetDebug(debug bool) *runner { +func (r *hrpRunner) SetDebug(debug bool) *hrpRunner { log.Info().Bool("debug", debug).Msg("[init] SetDebug") r.debug = debug return r } // SetProxyUrl configures the proxy URL, which is usually used to capture HTTP packets for debugging. -func (r *runner) SetProxyUrl(proxyUrl string) *runner { +func (r *hrpRunner) SetProxyUrl(proxyUrl string) *hrpRunner { log.Info().Str("proxyUrl", proxyUrl).Msg("[init] SetProxyUrl") p, err := url.Parse(proxyUrl) if err != nil { @@ -74,7 +74,7 @@ func (r *runner) SetProxyUrl(proxyUrl string) *runner { } // Run starts to execute one or multiple testcases. -func (r *runner) Run(testcases ...ITestCase) error { +func (r *hrpRunner) Run(testcases ...ITestCase) error { event := ga.EventTracking{ Category: "RunAPITests", Action: "hrp run", @@ -98,7 +98,7 @@ func (r *runner) Run(testcases ...ITestCase) error { return nil } -func (r *runner) runCase(testcase *TestCase) error { +func (r *hrpRunner) runCase(testcase *TestCase) error { config := testcase.Config if err := r.parseConfig(config); err != nil { return err @@ -117,12 +117,12 @@ func (r *runner) runCase(testcase *TestCase) error { return nil } -func (r *runner) runStep(step IStep, config *TConfig) (stepResult *stepData, err error) { - log.Info().Str("step", step.name()).Msg("run step start") +func (r *hrpRunner) runStep(step IStep, config *TConfig) (stepResult *stepData, err error) { + log.Info().Str("step", step.Name()).Msg("run step start") // copy step to avoid data racing 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") return } @@ -167,14 +167,14 @@ func (r *runner) runStep(step IStep, config *TConfig) (stepResult *stepData, err } log.Info(). - Str("step", step.name()). + Str("step", step.Name()). Bool("success", stepResult.success). Interface("exportVars", stepResult.exportVars). Msg("run step end") return } -func (r *runner) runStepRequest(step *TStep) (stepResult *stepData, err error) { +func (r *hrpRunner) runStepRequest(step *TStep) (stepResult *stepData, err error) { stepResult = &stepData{ name: step.Name, success: false, @@ -339,7 +339,7 @@ func (r *runner) runStepRequest(step *TStep) (stepResult *stepData, err error) { return } -func (r *runner) runStepTestCase(step *TStep) (stepResult *stepData, err error) { +func (r *hrpRunner) runStepTestCase(step *TStep) (stepResult *stepData, err error) { stepResult = &stepData{ name: step.Name, success: false, @@ -349,7 +349,7 @@ func (r *runner) runStepTestCase(step *TStep) (stepResult *stepData, err error) return } -func (r *runner) parseConfig(config *TConfig) error { +func (r *hrpRunner) parseConfig(config *TConfig) error { // parse config variables parsedVariables, err := parseVariables(config.Variables) if err != nil { @@ -375,7 +375,7 @@ func (r *runner) parseConfig(config *TConfig) error { return nil } -func (r *runner) getSummary() *testCaseSummary { +func (r *hrpRunner) getSummary() *testCaseSummary { return &testCaseSummary{} } diff --git a/step.go b/step.go index d0a6e54..909e08e 100644 --- a/step.go +++ b/step.go @@ -231,18 +231,18 @@ func (s *requestWithOptionalArgs) Extract() *stepRequestExtraction { } } -func (s *requestWithOptionalArgs) name() string { +func (s *requestWithOptionalArgs) Name() string { if s.step.Name != "" { return s.step.Name } return fmt.Sprintf("%s %s", s.step.Request.Method, s.step.Request.URL) } -func (s *requestWithOptionalArgs) getType() string { +func (s *requestWithOptionalArgs) Type() string { return fmt.Sprintf("request-%v", s.step.Request.Method) } -func (s *requestWithOptionalArgs) toStruct() *TStep { +func (s *requestWithOptionalArgs) ToStruct() *TStep { return s.step } @@ -263,17 +263,17 @@ func (s *testcaseWithOptionalArgs) Export(names ...string) *testcaseWithOptional return s } -func (s *testcaseWithOptionalArgs) name() string { +func (s *testcaseWithOptionalArgs) Name() string { if s.step.Name != "" { return s.step.Name } return s.step.TestCase.Config.Name } -func (s *testcaseWithOptionalArgs) getType() string { +func (s *testcaseWithOptionalArgs) Type() string { return "testcase" } -func (s *testcaseWithOptionalArgs) toStruct() *TStep { +func (s *testcaseWithOptionalArgs) ToStruct() *TStep { return s.step } diff --git a/validate.go b/validate.go index 5ae23cb..e522ac9 100644 --- a/validate.go +++ b/validate.go @@ -9,18 +9,18 @@ type stepRequestValidation struct { step *TStep } -func (s *stepRequestValidation) name() string { +func (s *stepRequestValidation) Name() string { if s.step.Name != "" { return s.step.Name } return fmt.Sprintf("%s %s", s.step.Request.Method, s.step.Request.URL) } -func (s *stepRequestValidation) getType() string { +func (s *stepRequestValidation) Type() string { return fmt.Sprintf("request-%v", s.step.Request.Method) } -func (s *stepRequestValidation) toStruct() *TStep { +func (s *stepRequestValidation) ToStruct() *TStep { return s.step }