Merge pull request #1397 from xucong053/bugfix

- fix: step name with parameterize mechanism
- fix: error of concurrent map writes occurred while uploading in boom mode
- fix: record all requests of testcase reference in boom mode
- fix: failed to record the step of occurring error in the html report
This commit is contained in:
debugtalk
2022-07-04 18:04:58 +08:00
committed by GitHub
28 changed files with 135 additions and 76 deletions

View File

@@ -1,5 +1,14 @@
# Release History # Release History
## v4.1.6 (2022-07-04)
**go version**
- fix: step name with parameterize mechanism
- fix: error of concurrent map writes occurred while uploading in boom mode
- fix: record all requests of testcase reference in boom mode
- fix: failed to record the step of occurring error in the html report
## v4.1.5 (2022-06-27) ## v4.1.5 (2022-06-27)
**go version** **go version**

View File

@@ -37,4 +37,4 @@ Copyright 2017 debugtalk
* [hrp startproject](hrp_startproject.md) - create a scaffold project * [hrp startproject](hrp_startproject.md) - create a scaffold project
* [hrp wiki](hrp_wiki.md) - visit https://httprunner.com * [hrp wiki](hrp_wiki.md) - visit https://httprunner.com
###### Auto generated by spf13/cobra on 27-Jun-2022 ###### Auto generated by spf13/cobra on 4-Jul-2022

View File

@@ -42,4 +42,4 @@ hrp boom [flags]
* [hrp](hrp.md) - Next-Generation API Testing Solution. * [hrp](hrp.md) - Next-Generation API Testing Solution.
###### Auto generated by spf13/cobra on 27-Jun-2022 ###### Auto generated by spf13/cobra on 4-Jul-2022

View File

@@ -28,4 +28,4 @@ hrp build $path ... [flags]
* [hrp](hrp.md) - Next-Generation API Testing Solution. * [hrp](hrp.md) - Next-Generation API Testing Solution.
###### Auto generated by spf13/cobra on 27-Jun-2022 ###### Auto generated by spf13/cobra on 4-Jul-2022

View File

@@ -22,4 +22,4 @@ hrp convert $path... [flags]
* [hrp](hrp.md) - Next-Generation API Testing Solution. * [hrp](hrp.md) - Next-Generation API Testing Solution.
###### Auto generated by spf13/cobra on 27-Jun-2022 ###### Auto generated by spf13/cobra on 4-Jul-2022

View File

@@ -16,4 +16,4 @@ hrp pytest $path ... [flags]
* [hrp](hrp.md) - Next-Generation API Testing Solution. * [hrp](hrp.md) - Next-Generation API Testing Solution.
###### Auto generated by spf13/cobra on 27-Jun-2022 ###### Auto generated by spf13/cobra on 4-Jul-2022

View File

@@ -35,4 +35,4 @@ hrp run $path... [flags]
* [hrp](hrp.md) - Next-Generation API Testing Solution. * [hrp](hrp.md) - Next-Generation API Testing Solution.
###### Auto generated by spf13/cobra on 27-Jun-2022 ###### Auto generated by spf13/cobra on 4-Jul-2022

View File

@@ -21,4 +21,4 @@ hrp startproject $project_name [flags]
* [hrp](hrp.md) - Next-Generation API Testing Solution. * [hrp](hrp.md) - Next-Generation API Testing Solution.
###### Auto generated by spf13/cobra on 27-Jun-2022 ###### Auto generated by spf13/cobra on 4-Jul-2022

View File

@@ -16,4 +16,4 @@ hrp wiki [flags]
* [hrp](hrp.md) - Next-Generation API Testing Solution. * [hrp](hrp.md) - Next-Generation API Testing Solution.
###### Auto generated by spf13/cobra on 27-Jun-2022 ###### Auto generated by spf13/cobra on 4-Jul-2022

View File

@@ -1,5 +1,5 @@
{ {
"project_name": "demo-empty-project", "project_name": "demo-empty-project",
"create_time": "2022-06-27T16:52:45.660111+08:00", "create_time": "2022-07-04T14:54:33.795693+08:00",
"hrp_version": "v4.1.4" "hrp_version": "v4.1.5"
} }

View File

@@ -1,5 +1,5 @@
{ {
"project_name": "demo-with-go-plugin", "project_name": "demo-with-go-plugin",
"create_time": "2022-06-27T16:51:52.779837+08:00", "create_time": "2022-07-04T14:53:59.755944+08:00",
"hrp_version": "v4.1.4" "hrp_version": "v4.1.5"
} }

View File

@@ -1,4 +1,4 @@
# NOTE: Generated By hrp v4.1.4, DO NOT EDIT! # NOTE: Generated By hrp v4.1.5, DO NOT EDIT!
import sys import sys
import os import os

View File

@@ -1,5 +1,5 @@
{ {
"project_name": "demo-with-py-plugin", "project_name": "demo-with-py-plugin",
"create_time": "2022-06-27T16:51:54.32061+08:00", "create_time": "2022-07-04T14:54:00.346082+08:00",
"hrp_version": "v4.1.4" "hrp_version": "v4.1.5"
} }

View File

@@ -1,5 +1,5 @@
{ {
"project_name": "demo-without-plugin", "project_name": "demo-without-plugin",
"create_time": "2022-06-27T16:52:45.363472+08:00", "create_time": "2022-07-04T14:54:33.495643+08:00",
"hrp_version": "v4.1.4" "hrp_version": "v4.1.5"
} }

View File

@@ -31,7 +31,7 @@
}, },
"teststeps": [ "teststeps": [
{ {
"name": "get with params", "name": "get with user: $username",
"variables": { "variables": {
"foo1": "$username", "foo1": "$username",
"foo2": "$password", "foo2": "$password",

View File

@@ -20,7 +20,7 @@ config:
verify: False verify: False
teststeps: teststeps:
- name: get with params - name: "get with user: $username"
variables: variables:
foo1: $username foo1: $username
foo2: $password foo2: $password

View File

@@ -125,18 +125,38 @@ func (b *HRPBoomer) convertBoomerTask(testcase *TestCase, rendezvousList []*Rend
startTime := time.Now() startTime := time.Now()
for _, step := range testcase.TestSteps { for _, step := range testcase.TestSteps {
// parse step name
parsedName, err := sessionRunner.parser.ParseString(step.Name(), sessionRunner.sessionVariables)
if err != nil {
parsedName = step.Name()
}
stepName := convertString(parsedName)
// reset start time only once before step // reset start time only once before step
once.Do(func() { once.Do(func() {
b.Boomer.ResetStartTime() b.Boomer.ResetStartTime()
}) })
stepResult, err := step.Run(sessionRunner) stepResult, err := step.Run(sessionRunner)
// update step result name with parsed step name
stepResult.Name = stepName
// record requests result of the step if step type is testcase
if stepResult.StepType == stepTypeTestCase && stepResult.Data != nil {
// record requests of testcase step
for _, result := range stepResult.Data.([]*StepResult) {
if result.Success {
b.RecordSuccess(string(result.StepType), result.Name, result.Elapsed, result.ContentSize)
} else {
b.RecordFailure(string(result.StepType), result.Name, result.Elapsed, result.Attachment)
}
}
}
// record step failure
if err != nil { if err != nil {
// step failed // step failed
var elapsed int64 var elapsed int64
if stepResult != nil { if stepResult != nil {
elapsed = stepResult.Elapsed elapsed = stepResult.Elapsed
} }
b.RecordFailure(string(step.Type()), step.Name(), elapsed, err.Error()) b.RecordFailure(string(step.Type()), stepResult.Name, elapsed, err.Error())
// update flag // update flag
testcaseSuccess = false testcaseSuccess = false
@@ -150,7 +170,7 @@ func (b *HRPBoomer) convertBoomerTask(testcase *TestCase, rendezvousList []*Rend
continue continue
} }
// step success // record step success
if stepResult.StepType == stepTypeTransaction { if stepResult.StepType == stepTypeTransaction {
// transaction // transaction
// FIXME: support nested transactions // FIXME: support nested transactions
@@ -165,7 +185,7 @@ func (b *HRPBoomer) convertBoomerTask(testcase *TestCase, rendezvousList []*Rend
// no record required // no record required
} else { } else {
// request or testcase step // request or testcase step
b.RecordSuccess(string(step.Type()), step.Name(), stepResult.Elapsed, stepResult.ContentSize) b.RecordSuccess(string(step.Type()), stepResult.Name, stepResult.Elapsed, stepResult.ContentSize)
// update extracted variables // update extracted variables
for k, v := range stepResult.ExportVars { for k, v := range stepResult.ExportVars {
sessionRunner.sessionVariables[k] = v sessionRunner.sessionVariables[k] = v

View File

@@ -74,12 +74,16 @@ func (s *requestStats) logTransaction(name string, success bool, responseTime in
} }
func (s *requestStats) logRequest(method, name string, responseTime int64, contentLength int64) { func (s *requestStats) logRequest(method, name string, responseTime int64, contentLength int64) {
s.total.log(responseTime, contentLength) if method != "testcase" {
s.total.log(responseTime, contentLength)
}
s.get(name, method).log(responseTime, contentLength) s.get(name, method).log(responseTime, contentLength)
} }
func (s *requestStats) logError(method, name, err string) { func (s *requestStats) logError(method, name, err string) {
s.total.logFailures() if method != "testcase" {
s.total.logFailures()
}
s.get(name, method).logFailures() s.get(name, method).logFailures()
// store error in errors map // store error in errors map

View File

@@ -1,4 +1,4 @@
# NOTE: Generated By hrp v4.1.4, DO NOT EDIT! # NOTE: Generated By hrp v4.1.5, DO NOT EDIT!
import sys import sys
import os import os

View File

@@ -1,4 +1,4 @@
// NOTE: Generated By hrp v4.1.4, DO NOT EDIT! // NOTE: Generated By hrp v4.1.5, DO NOT EDIT!
package main package main
import ( import (

View File

@@ -64,10 +64,17 @@ func (r *SessionRunner) Start(givenVars map[string]interface{}) error {
// run step in sequential order // run step in sequential order
for _, step := range r.testCase.TestSteps { for _, step := range r.testCase.TestSteps {
log.Info().Str("step", step.Name()). // parse step name
parsedName, err := r.parser.ParseString(step.Name(), r.sessionVariables)
if err != nil {
parsedName = step.Name()
}
stepName := convertString(parsedName)
log.Info().Str("step", stepName).
Str("type", string(step.Type())).Msg("run step start") Str("type", string(step.Type())).Msg("run step start")
stepResult, err := step.Run(r) stepResult, err := step.Run(r)
stepResult.Name = stepName
if err != nil { if err != nil {
log.Error(). log.Error().
Str("step", stepResult.Name). Str("step", stepResult.Name).

View File

@@ -117,6 +117,10 @@ func extendWithAPI(testStep *TStep, overriddenStep *API) {
} }
// merge & override request // merge & override request
testStep.Request = overriddenStep.Request testStep.Request = overriddenStep.Request
// init upload
if testStep.Request.Upload != nil {
initUpload(testStep)
}
// merge & override variables // merge & override variables
testStep.Variables = mergeVariables(testStep.Variables, overriddenStep.Variables) testStep.Variables = mergeVariables(testStep.Variables, overriddenStep.Variables)
// merge & override extractors // merge & override extractors

View File

@@ -259,24 +259,28 @@ func (r *requestBuilder) prepareBody(stepVariables map[string]interface{}) error
return nil return nil
} }
func prepareUpload(parser *Parser, step *TStep) (err error) { func initUpload(step *TStep) {
if step.Request.Upload == nil {
return
}
step.Request.Upload, err = parser.ParseVariables(step.Request.Upload)
if err != nil {
return
}
if step.Variables == nil {
step.Variables = make(map[string]interface{})
}
step.Variables["m_upload"] = step.Request.Upload
step.Variables["m_encoder"] = fmt.Sprintf("${multipart_encoder($m_upload)}")
if step.Request.Headers == nil { if step.Request.Headers == nil {
step.Request.Headers = make(map[string]string) step.Request.Headers = make(map[string]string)
} }
step.Request.Headers["Content-Type"] = "${multipart_content_type($m_encoder)}" step.Request.Headers["Content-Type"] = "${multipart_content_type($m_encoder)}"
step.Request.Body = "$m_encoder" step.Request.Body = "$m_encoder"
}
func prepareUpload(parser *Parser, step *TStep, stepVariables map[string]interface{}) (err error) {
if step.Request.Upload == nil {
return
}
uploadMap, err := parser.Parse(step.Request.Upload, stepVariables)
if err != nil {
return
}
stepVariables["m_upload"] = uploadMap
mEncoder, err := parser.Parse("${multipart_encoder($m_upload)}", stepVariables)
if err != nil {
return
}
stepVariables["m_encoder"] = mEncoder
return return
} }
@@ -293,15 +297,25 @@ func runStepRequest(r *SessionRunner, step *TStep) (stepResult *StepResult, err
if err != nil { if err != nil {
stepResult.Attachment = err.Error() stepResult.Attachment = err.Error()
} }
// update summary
r.summary.Records = append(r.summary.Records, stepResult)
r.summary.Stat.Total += 1
if stepResult.Success {
r.summary.Stat.Successes += 1
} else {
r.summary.Stat.Failures += 1
// update summary result to failed
r.summary.Success = false
}
}() }()
err = prepareUpload(r.parser, step) // override step variables
stepVariables, err := r.MergeStepVariables(step.Variables)
if err != nil { if err != nil {
return return
} }
// override step variables err = prepareUpload(r.parser, step, stepVariables)
stepVariables, err := r.MergeStepVariables(step.Variables)
if err != nil { if err != nil {
return return
} }
@@ -435,17 +449,6 @@ func runStepRequest(r *SessionRunner, step *TStep) (stepResult *StepResult, err
stepResult.ContentSize = resp.ContentLength stepResult.ContentSize = resp.ContentLength
stepResult.Data = sessionData stepResult.Data = sessionData
// update summary
r.summary.Records = append(r.summary.Records, stepResult)
r.summary.Stat.Total += 1
if stepResult.Success {
r.summary.Stat.Successes += 1
} else {
r.summary.Stat.Failures += 1
// update summary result to failed
r.summary.Success = false
}
return stepResult, err return stepResult, err
} }
@@ -822,6 +825,8 @@ func (s *StepRequestWithOptionalArgs) WithBody(body interface{}) *StepRequestWit
// WithUpload sets HTTP request body for uploading file(s). // WithUpload sets HTTP request body for uploading file(s).
func (s *StepRequestWithOptionalArgs) WithUpload(upload map[string]interface{}) *StepRequestWithOptionalArgs { func (s *StepRequestWithOptionalArgs) WithUpload(upload map[string]interface{}) *StepRequestWithOptionalArgs {
// init upload
initUpload(s.step)
s.step.Request.Upload = upload s.step.Request.Upload = upload
return s return s
} }

View File

@@ -44,13 +44,20 @@ func (s *StepTestCaseWithOptionalArgs) Struct() *TStep {
return s.step return s.step
} }
func (s *StepTestCaseWithOptionalArgs) Run(r *SessionRunner) (*StepResult, error) { func (s *StepTestCaseWithOptionalArgs) Run(r *SessionRunner) (stepResult *StepResult, err error) {
stepResult := &StepResult{ stepResult = &StepResult{
Name: s.step.Name, Name: s.step.Name,
StepType: stepTypeTestCase, StepType: stepTypeTestCase,
Success: false, Success: false,
} }
defer func() {
// update testcase summary
if err != nil {
stepResult.Attachment = err.Error()
}
}()
stepVariables, err := r.MergeStepVariables(s.step.Variables) stepVariables, err := r.MergeStepVariables(s.step.Variables)
if err != nil { if err != nil {
return stepResult, err return stepResult, err
@@ -82,12 +89,10 @@ func (s *StepTestCaseWithOptionalArgs) Run(r *SessionRunner) (*StepResult, error
start := time.Now() start := time.Now()
// run referenced testcase with step variables // run referenced testcase with step variables
err = sessionRunner.Start(stepVariables) err = sessionRunner.Start(stepVariables)
stepResult.Elapsed = time.Since(start).Milliseconds() if err == nil {
if err != nil { stepResult.Success = true
stepResult.Attachment = err.Error()
r.summary.Success = false
return stepResult, err
} }
stepResult.Elapsed = time.Since(start).Milliseconds()
summary := sessionRunner.GetSummary() summary := sessionRunner.GetSummary()
// update step names // update step names
for _, record := range summary.Records { for _, record := range summary.Records {
@@ -96,7 +101,6 @@ func (s *StepTestCaseWithOptionalArgs) Run(r *SessionRunner) (*StepResult, error
stepResult.Data = summary.Records stepResult.Data = summary.Records
// export testcase export variables // export testcase export variables
stepResult.ExportVars = summary.InOut.ExportVars stepResult.ExportVars = summary.InOut.ExportVars
stepResult.Success = true
// merge testcase summary // merge testcase summary
r.summary.Records = append(r.summary.Records, summary.Records...) r.summary.Records = append(r.summary.Records, summary.Records...)
@@ -104,5 +108,5 @@ func (s *StepTestCaseWithOptionalArgs) Run(r *SessionRunner) (*StepResult, error
r.summary.Stat.Successes += summary.Stat.Successes r.summary.Stat.Successes += summary.Stat.Successes
r.summary.Stat.Failures += summary.Stat.Failures r.summary.Stat.Failures += summary.Stat.Failures
return stepResult, nil return stepResult, err
} }

View File

@@ -239,6 +239,16 @@ func runStepWebSocket(r *SessionRunner, step *TStep) (stepResult *StepResult, er
if err != nil { if err != nil {
stepResult.Attachment = err.Error() stepResult.Attachment = err.Error()
} }
// update summary
r.summary.Records = append(r.summary.Records, stepResult)
r.summary.Stat.Total += 1
if stepResult.Success {
r.summary.Stat.Successes += 1
} else {
r.summary.Stat.Failures += 1
// update summary result to failed
r.summary.Success = false
}
}() }()
// override step variables // override step variables
@@ -385,16 +395,6 @@ func runStepWebSocket(r *SessionRunner, step *TStep) (stepResult *StepResult, er
stepResult.Success = true stepResult.Success = true
} }
// update summary
r.summary.Records = append(r.summary.Records, stepResult)
r.summary.Stat.Total += 1
if stepResult.Success {
r.summary.Stat.Successes += 1
} else {
r.summary.Stat.Failures += 1
// update summary result to failed
r.summary.Success = false
}
return stepResult, nil return stepResult, nil
} }

View File

@@ -199,5 +199,6 @@ func newSummary() *TestCaseSummary {
Stat: &TestStepStat{}, Stat: &TestStepStat{},
Time: &TestCaseTime{}, Time: &TestCaseTime{},
InOut: &TestCaseInOut{}, InOut: &TestCaseInOut{},
Records: []*StepResult{},
} }
} }

View File

@@ -144,6 +144,10 @@ func (path *TestCasePath) ToTestCase() (*TestCase, error) {
step: step, step: step,
}) })
} else if step.Request != nil { } else if step.Request != nil {
// init upload
if step.Request.Upload != nil {
initUpload(step)
}
testCase.TestSteps = append(testCase.TestSteps, &StepRequestWithOptionalArgs{ testCase.TestSteps = append(testCase.TestSteps, &StepRequestWithOptionalArgs{
step: step, step: step,
}) })

View File

@@ -9,12 +9,13 @@ import (
func TestCaseUploadFile(t *testing.T) { func TestCaseUploadFile(t *testing.T) {
testcase := &hrp.TestCase{ testcase := &hrp.TestCase{
Config: hrp.NewConfig("test upload file to httpbin"). Config: hrp.NewConfig("test upload file to httpbin").
SetBaseURL("https://httpbin.org"), SetBaseURL("https://httpbin.org").
WithVariables(map[string]interface{}{"upload_file": "test.env"}),
TestSteps: []hrp.IStep{ TestSteps: []hrp.IStep{
hrp.NewStep("upload file"). hrp.NewStep("upload file").
WithVariables(map[string]interface{}{ WithVariables(map[string]interface{}{
"m_encoder": "${multipart_encoder($m_upload)}", "m_encoder": "${multipart_encoder($m_upload)}",
"m_upload": map[string]interface{}{"file": "test.env"}, "m_upload": map[string]interface{}{"file": "$upload_file"},
}). }).
POST("/post"). POST("/post").
WithHeaders(map[string]string{"Content-Type": "${multipart_content_type($m_encoder)}"}). WithHeaders(map[string]string{"Content-Type": "${multipart_content_type($m_encoder)}"}).
@@ -24,7 +25,7 @@ func TestCaseUploadFile(t *testing.T) {
AssertStartsWith("body.files.file", "UserName=test", "check uploaded file"), AssertStartsWith("body.files.file", "UserName=test", "check uploaded file"),
hrp.NewStep("upload file with keyword"). hrp.NewStep("upload file with keyword").
POST("/post"). POST("/post").
WithUpload(map[string]interface{}{"file": "test.env"}). WithUpload(map[string]interface{}{"file": "$upload_file"}).
Validate(). Validate().
AssertEqual("status_code", 200, "check status code"). AssertEqual("status_code", 200, "check status code").
AssertStartsWith("body.files.file", "UserName=test", "check uploaded file"), AssertStartsWith("body.files.file", "UserName=test", "check uploaded file"),