mirror of
https://github.com/httprunner/httprunner.git
synced 2026-05-12 02:21:29 +08:00
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:
@@ -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**
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
},
|
||||
"teststeps": [
|
||||
{
|
||||
"name": "get with params",
|
||||
"name": "get with user: $username",
|
||||
"variables": {
|
||||
"foo1": "$username",
|
||||
"foo2": "$password",
|
||||
|
||||
@@ -20,7 +20,7 @@ config:
|
||||
verify: False
|
||||
|
||||
teststeps:
|
||||
- name: get with params
|
||||
- name: "get with user: $username"
|
||||
variables:
|
||||
foo1: $username
|
||||
foo2: $password
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 (
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -199,5 +199,6 @@ func newSummary() *TestCaseSummary {
|
||||
Stat: &TestStepStat{},
|
||||
Time: &TestCaseTime{},
|
||||
InOut: &TestCaseInOut{},
|
||||
Records: []*StepResult{},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
})
|
||||
|
||||
@@ -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"),
|
||||
|
||||
Reference in New Issue
Block a user