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
## 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)
**go version**

View File

@@ -37,4 +37,4 @@ Copyright 2017 debugtalk
* [hrp startproject](hrp_startproject.md) - create a scaffold project
* [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.
###### 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.
###### 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.
###### 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.
###### 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.
###### 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.
###### 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.
###### 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",
"create_time": "2022-06-27T16:52:45.660111+08:00",
"hrp_version": "v4.1.4"
"create_time": "2022-07-04T14:54:33.795693+08:00",
"hrp_version": "v4.1.5"
}

View File

@@ -1,5 +1,5 @@
{
"project_name": "demo-with-go-plugin",
"create_time": "2022-06-27T16:51:52.779837+08:00",
"hrp_version": "v4.1.4"
"create_time": "2022-07-04T14:53:59.755944+08:00",
"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 os

View File

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

View File

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

View File

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

View File

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

View File

@@ -125,18 +125,38 @@ func (b *HRPBoomer) convertBoomerTask(testcase *TestCase, rendezvousList []*Rend
startTime := time.Now()
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
once.Do(func() {
b.Boomer.ResetStartTime()
})
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 {
// step failed
var elapsed int64
if stepResult != nil {
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
testcaseSuccess = false
@@ -150,7 +170,7 @@ func (b *HRPBoomer) convertBoomerTask(testcase *TestCase, rendezvousList []*Rend
continue
}
// step success
// record step success
if stepResult.StepType == stepTypeTransaction {
// transaction
// FIXME: support nested transactions
@@ -165,7 +185,7 @@ func (b *HRPBoomer) convertBoomerTask(testcase *TestCase, rendezvousList []*Rend
// no record required
} else {
// 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
for k, v := range stepResult.ExportVars {
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) {
s.total.log(responseTime, contentLength)
if method != "testcase" {
s.total.log(responseTime, contentLength)
}
s.get(name, method).log(responseTime, contentLength)
}
func (s *requestStats) logError(method, name, err string) {
s.total.logFailures()
if method != "testcase" {
s.total.logFailures()
}
s.get(name, method).logFailures()
// 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 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
import (

View File

@@ -64,10 +64,17 @@ func (r *SessionRunner) Start(givenVars map[string]interface{}) error {
// run step in sequential order
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")
stepResult, err := step.Run(r)
stepResult.Name = stepName
if err != nil {
log.Error().
Str("step", stepResult.Name).

View File

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

View File

@@ -259,24 +259,28 @@ func (r *requestBuilder) prepareBody(stepVariables map[string]interface{}) error
return nil
}
func prepareUpload(parser *Parser, step *TStep) (err error) {
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)}")
func initUpload(step *TStep) {
if step.Request.Headers == nil {
step.Request.Headers = make(map[string]string)
}
step.Request.Headers["Content-Type"] = "${multipart_content_type($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
}
@@ -293,15 +297,25 @@ func runStepRequest(r *SessionRunner, step *TStep) (stepResult *StepResult, err
if err != nil {
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 {
return
}
// override step variables
stepVariables, err := r.MergeStepVariables(step.Variables)
err = prepareUpload(r.parser, step, stepVariables)
if err != nil {
return
}
@@ -435,17 +449,6 @@ func runStepRequest(r *SessionRunner, step *TStep) (stepResult *StepResult, err
stepResult.ContentSize = resp.ContentLength
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
}
@@ -822,6 +825,8 @@ func (s *StepRequestWithOptionalArgs) WithBody(body interface{}) *StepRequestWit
// WithUpload sets HTTP request body for uploading file(s).
func (s *StepRequestWithOptionalArgs) WithUpload(upload map[string]interface{}) *StepRequestWithOptionalArgs {
// init upload
initUpload(s.step)
s.step.Request.Upload = upload
return s
}

View File

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

View File

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

View File

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

View File

@@ -9,12 +9,13 @@ import (
func TestCaseUploadFile(t *testing.T) {
testcase := &hrp.TestCase{
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{
hrp.NewStep("upload file").
WithVariables(map[string]interface{}{
"m_encoder": "${multipart_encoder($m_upload)}",
"m_upload": map[string]interface{}{"file": "test.env"},
"m_upload": map[string]interface{}{"file": "$upload_file"},
}).
POST("/post").
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"),
hrp.NewStep("upload file with keyword").
POST("/post").
WithUpload(map[string]interface{}{"file": "test.env"}).
WithUpload(map[string]interface{}{"file": "$upload_file"}).
Validate().
AssertEqual("status_code", 200, "check status code").
AssertStartsWith("body.files.file", "UserName=test", "check uploaded file"),