mirror of
https://github.com/httprunner/httprunner.git
synced 2026-06-06 00:09:37 +08:00
merge master
This commit is contained in:
@@ -5,10 +5,12 @@
|
|||||||
**go version**
|
**go version**
|
||||||
|
|
||||||
- feat: support setting global testcase timeout and step timeout
|
- feat: support setting global testcase timeout and step timeout
|
||||||
|
- feat: support uploading file by multipart/form-data
|
||||||
- change: set http request timeout default to 120s
|
- change: set http request timeout default to 120s
|
||||||
- fix: insert response cookies into request for redirect requests
|
- fix: insert response cookies into request for redirect requests
|
||||||
- fix: support log debug level for load testing
|
- fix: support log debug level for load testing
|
||||||
- fix: failed to load json/data content in api reference
|
- fix: failed to load json/data content in api reference
|
||||||
|
- fix: failed to convert postman collection containing multipart/form-data requests to pytest
|
||||||
|
|
||||||
## v4.1.4 (2022-06-17)
|
## v4.1.4 (2022-06-17)
|
||||||
|
|
||||||
|
|||||||
1
examples/data/postman/intro.txt
Normal file
1
examples/data/postman/intro.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
HttpRunner is an open source API testing tool that supports HTTP(S)/HTTP2/WebSocket/RPC network protocols, covering API testing, performance testing and digital experience monitoring (DEM) test types. Enjoy!
|
||||||
BIN
examples/data/postman/logo.jpeg
Normal file
BIN
examples/data/postman/logo.jpeg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 316 KiB |
@@ -230,6 +230,16 @@
|
|||||||
"value": "v3",
|
"value": "v3",
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"disabled": true
|
"disabled": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "intro_key",
|
||||||
|
"type": "file",
|
||||||
|
"src": "intro.txt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "logo_key",
|
||||||
|
"type": "file",
|
||||||
|
"src": "logo.jpeg"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,25 +1,34 @@
|
|||||||
package builtin
|
package builtin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"crypto/md5"
|
"crypto/md5"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
|
"mime/multipart"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
var Functions = map[string]interface{}{
|
var Functions = map[string]interface{}{
|
||||||
"get_timestamp": getTimestamp, // call without arguments
|
"get_timestamp": getTimestamp, // call without arguments
|
||||||
"sleep": sleep, // call with one argument
|
"sleep": sleep, // call with one argument
|
||||||
"gen_random_string": genRandomString, // call with one argument
|
"gen_random_string": genRandomString, // call with one argument
|
||||||
"max": math.Max, // call with two arguments
|
"max": math.Max, // call with two arguments
|
||||||
"md5": MD5, // call with one argument
|
"md5": MD5, // call with one argument
|
||||||
"parameterize": loadFromCSV,
|
"parameterize": loadFromCSV,
|
||||||
"P": loadFromCSV,
|
"P": loadFromCSV,
|
||||||
"environ": os.Getenv,
|
"environ": os.Getenv,
|
||||||
"ENV": os.Getenv,
|
"ENV": os.Getenv,
|
||||||
"load_ws_message": loadMessage,
|
"load_ws_message": loadMessage,
|
||||||
|
"multipart_encoder": multipartEncoder,
|
||||||
|
"multipart_content_type": multipartContentType,
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -50,3 +59,60 @@ func MD5(str string) string {
|
|||||||
hasher.Write([]byte(str))
|
hasher.Write([]byte(str))
|
||||||
return hex.EncodeToString(hasher.Sum(nil))
|
return hex.EncodeToString(hasher.Sum(nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type TFormWriter struct {
|
||||||
|
Writer *multipart.Writer
|
||||||
|
Payload *bytes.Buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
func multipartEncoder(formMap map[string]interface{}) *TFormWriter {
|
||||||
|
payload := &bytes.Buffer{}
|
||||||
|
writer := multipart.NewWriter(payload)
|
||||||
|
for formKey, formValue := range formMap {
|
||||||
|
formValueString := fmt.Sprintf("%v", formValue)
|
||||||
|
if err := writeFormDataFile(writer, formKey, formValueString); err == nil {
|
||||||
|
// form value is a file path
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// form value is not a file path, write as raw string
|
||||||
|
if err := writer.WriteField(formKey, formValueString); err != nil {
|
||||||
|
log.Info().Err(err).Msgf("failed to write field: %v=%v, ignore", formKey, formValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := writer.Close(); err != nil {
|
||||||
|
}
|
||||||
|
return &TFormWriter{
|
||||||
|
Writer: writer,
|
||||||
|
Payload: payload,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeFormDataFile(writer *multipart.Writer, fName, fPath string) error {
|
||||||
|
var err error
|
||||||
|
fPath, err = filepath.Abs(fPath)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Str("path", fPath).Msg("convert absolute path failed")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !IsFilePathExists(fPath) {
|
||||||
|
return errors.Errorf("file %v not existed", fPath)
|
||||||
|
}
|
||||||
|
file, err := os.ReadFile(fPath)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Str("path", fPath).Msg("read file failed")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
formFile, err := writer.CreateFormFile(fName, filepath.Base(fPath))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = formFile.Write(file)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func multipartContentType(w *TFormWriter) string {
|
||||||
|
if w.Writer == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return w.Writer.FormDataContentType()
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,13 +1,8 @@
|
|||||||
package convert
|
package convert
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"mime/multipart"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -158,7 +153,7 @@ func (c *ConverterPostman) ToYAML() (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *ConverterPostman) ToGoTest() (string, error) {
|
func (c *ConverterPostman) ToGoTest() (string, error) {
|
||||||
//TODO implement me
|
// TODO implement me
|
||||||
return "", errors.New("convert from postman to gotest scripts is not supported yet")
|
return "", errors.New("convert from postman to gotest scripts is not supported yet")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -208,7 +203,7 @@ func (c *ConverterPostman) prepareTestSteps(casePostman *CasePostman) ([]*hrp.TS
|
|||||||
|
|
||||||
var steps []*hrp.TStep
|
var steps []*hrp.TStep
|
||||||
for _, item := range itemList {
|
for _, item := range itemList {
|
||||||
step, err := c.prepareTestStep(&item, steps)
|
step, err := c.prepareTestStep(&item)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -234,7 +229,7 @@ func extractItemList(item TItem, itemList *[]TItem) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ConverterPostman) prepareTestStep(item *TItem, steps []*hrp.TStep) (*hrp.TStep, error) {
|
func (c *ConverterPostman) prepareTestStep(item *TItem) (*hrp.TStep, error) {
|
||||||
log.Info().
|
log.Info().
|
||||||
Str("method", item.Request.Method).
|
Str("method", item.Request.Method).
|
||||||
Str("url", item.Request.URL.Raw).
|
Str("url", item.Request.URL.Raw).
|
||||||
@@ -265,7 +260,7 @@ func (c *ConverterPostman) prepareTestStep(item *TItem, steps []*hrp.TStep) (*hr
|
|||||||
if err := step.makeRequestCookies(item); err != nil {
|
if err := step.makeRequestCookies(item); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err := step.makeRequestBody(item, steps); err != nil {
|
if err := step.makeRequestBody(item); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &step.TStep, nil
|
return &step.TStep, nil
|
||||||
@@ -373,7 +368,7 @@ func (s *stepFromPostman) parseRequestCookiesMap(cookies string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *stepFromPostman) makeRequestBody(item *TItem, steps []*hrp.TStep) error {
|
func (s *stepFromPostman) makeRequestBody(item *TItem) error {
|
||||||
mode := item.Request.Body.Mode
|
mode := item.Request.Body.Mode
|
||||||
if mode == "" {
|
if mode == "" {
|
||||||
return nil
|
return nil
|
||||||
@@ -382,7 +377,7 @@ func (s *stepFromPostman) makeRequestBody(item *TItem, steps []*hrp.TStep) error
|
|||||||
case enumBodyRaw:
|
case enumBodyRaw:
|
||||||
return s.makeRequestBodyRaw(item)
|
return s.makeRequestBodyRaw(item)
|
||||||
case enumBodyFormData:
|
case enumBodyFormData:
|
||||||
return s.makeRequestBodyFormData(item, steps)
|
return s.makeRequestBodyFormData(item)
|
||||||
case enumBodyUrlEncoded:
|
case enumBodyUrlEncoded:
|
||||||
return s.makeRequestBodyUrlEncoded(item)
|
return s.makeRequestBodyUrlEncoded(item)
|
||||||
case enumBodyFile, enumBodyGraphQL:
|
case enumBodyFile, enumBodyGraphQL:
|
||||||
@@ -424,49 +419,22 @@ func (s *stepFromPostman) makeRequestBodyRaw(item *TItem) (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *stepFromPostman) makeRequestBodyFormData(item *TItem, steps []*hrp.TStep) (err error) {
|
func (s *stepFromPostman) makeRequestBodyFormData(item *TItem) error {
|
||||||
defer func() {
|
s.Request.Upload = make(map[string]interface{})
|
||||||
if err != nil {
|
|
||||||
err = errors.Wrap(err, "make request body form-data failed")
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
payload := &bytes.Buffer{}
|
|
||||||
writer := multipart.NewWriter(payload)
|
|
||||||
for _, field := range item.Request.Body.FormData {
|
for _, field := range item.Request.Body.FormData {
|
||||||
if field.Disabled {
|
if field.Disabled {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// form data could be text or file
|
// form data could be text or file
|
||||||
if field.Type == enumFieldTypeText {
|
if field.Type == enumFieldTypeText {
|
||||||
err = writer.WriteField(field.Key, field.Value)
|
s.Request.Upload[field.Key] = field.Value
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
} else if field.Type == enumFieldTypeFile {
|
} else if field.Type == enumFieldTypeFile {
|
||||||
err = writeFormDataFile(writer, &field)
|
s.Request.Upload[field.Key] = field.Src
|
||||||
if err != nil {
|
} else {
|
||||||
return
|
return errors.Errorf("make request body form data failed: unexpect field type: %v", field.Type)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
err = writer.Close()
|
return nil
|
||||||
s.Request.Body = payload.String()
|
|
||||||
s.Request.Headers["Content-Type"] = writer.FormDataContentType()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func writeFormDataFile(writer *multipart.Writer, field *TField) error {
|
|
||||||
file, err := os.Open(field.Src)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer file.Close()
|
|
||||||
formFile, err := writer.CreateFormFile(field.Key, filepath.Base(field.Src))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
_, err = io.Copy(formFile, file)
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *stepFromPostman) makeRequestBodyUrlEncoded(item *TItem) error {
|
func (s *stepFromPostman) makeRequestBodyUrlEncoded(item *TItem) error {
|
||||||
@@ -482,7 +450,7 @@ func (s *stepFromPostman) makeRequestBodyUrlEncoded(item *TItem) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO makeValidate from example response
|
// TODO makeValidate from test scripts
|
||||||
func (s *stepFromPostman) makeValidate(item *TItem) error {
|
func (s *stepFromPostman) makeValidate(item *TItem) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,11 +71,8 @@ func TestMakeTestCaseFromCollection(t *testing.T) {
|
|||||||
if !assert.Equal(t, "v1", tCase.TestSteps[0].Request.Params["k1"]) {
|
if !assert.Equal(t, "v1", tCase.TestSteps[0].Request.Params["k1"]) {
|
||||||
t.Fatal()
|
t.Fatal()
|
||||||
}
|
}
|
||||||
// check cookies (pass, postman collection doesn't contains cookies)
|
// check cookies (pass, postman collection doesn't contain cookies)
|
||||||
// check headers
|
// check headers
|
||||||
if !assert.Contains(t, tCase.TestSteps[1].Request.Headers["Content-Type"], "multipart/form-data") {
|
|
||||||
t.Fatal()
|
|
||||||
}
|
|
||||||
if !assert.Equal(t, "application/x-www-form-urlencoded", tCase.TestSteps[2].Request.Headers["Content-Type"]) {
|
if !assert.Equal(t, "application/x-www-form-urlencoded", tCase.TestSteps[2].Request.Headers["Content-Type"]) {
|
||||||
t.Fatal()
|
t.Fatal()
|
||||||
}
|
}
|
||||||
@@ -92,9 +89,6 @@ func TestMakeTestCaseFromCollection(t *testing.T) {
|
|||||||
if !assert.Equal(t, nil, tCase.TestSteps[0].Request.Body) {
|
if !assert.Equal(t, nil, tCase.TestSteps[0].Request.Body) {
|
||||||
t.Fatal()
|
t.Fatal()
|
||||||
}
|
}
|
||||||
if !assert.NotEmpty(t, tCase.TestSteps[1].Request.Body) {
|
|
||||||
t.Fatal()
|
|
||||||
}
|
|
||||||
if !assert.Equal(t, map[string]string{"k1": "v1", "k2": "v2"}, tCase.TestSteps[2].Request.Body) {
|
if !assert.Equal(t, map[string]string{"k1": "v1", "k2": "v2"}, tCase.TestSteps[2].Request.Body) {
|
||||||
t.Fatal()
|
t.Fatal()
|
||||||
}
|
}
|
||||||
@@ -121,9 +115,6 @@ func TestMakeTestCaseWithProfileOverride(t *testing.T) {
|
|||||||
if step.Request.Method == "GET" && !assert.Len(t, step.Request.Headers, 1) {
|
if step.Request.Method == "GET" && !assert.Len(t, step.Request.Headers, 1) {
|
||||||
t.Fatal()
|
t.Fatal()
|
||||||
}
|
}
|
||||||
if step.Request.Method == "POST" && !assert.Len(t, step.Request.Headers, 2) {
|
|
||||||
t.Fatal()
|
|
||||||
}
|
|
||||||
if !assert.Equal(t, "all original headers will be overridden", step.Request.Headers["Header1"]) {
|
if !assert.Equal(t, "all original headers will be overridden", step.Request.Headers["Header1"]) {
|
||||||
t.Fatal()
|
t.Fatal()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ type Request struct {
|
|||||||
Timeout float64 `json:"timeout,omitempty" yaml:"timeout,omitempty"` // timeout in seconds
|
Timeout float64 `json:"timeout,omitempty" yaml:"timeout,omitempty"` // timeout in seconds
|
||||||
AllowRedirects bool `json:"allow_redirects,omitempty" yaml:"allow_redirects,omitempty"`
|
AllowRedirects bool `json:"allow_redirects,omitempty" yaml:"allow_redirects,omitempty"`
|
||||||
Verify bool `json:"verify,omitempty" yaml:"verify,omitempty"`
|
Verify bool `json:"verify,omitempty" yaml:"verify,omitempty"`
|
||||||
|
Upload map[string]interface{} `json:"upload,omitempty" yaml:"upload,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func newRequestBuilder(parser *Parser, config *TConfig, stepRequest *Request) *requestBuilder {
|
func newRequestBuilder(parser *Parser, config *TConfig, stepRequest *Request) *requestBuilder {
|
||||||
@@ -246,6 +247,8 @@ func (r *requestBuilder) prepareBody(stepVariables map[string]interface{}) error
|
|||||||
dataBytes = vv
|
dataBytes = vv
|
||||||
case bytes.Buffer:
|
case bytes.Buffer:
|
||||||
dataBytes = vv.Bytes()
|
dataBytes = vv.Bytes()
|
||||||
|
case *builtin.TFormWriter:
|
||||||
|
dataBytes = vv.Payload.Bytes()
|
||||||
default: // unexpected body type
|
default: // unexpected body type
|
||||||
return errors.New("unexpected request body type")
|
return errors.New("unexpected request body type")
|
||||||
}
|
}
|
||||||
@@ -256,6 +259,27 @@ func (r *requestBuilder) prepareBody(stepVariables map[string]interface{}) error
|
|||||||
return nil
|
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)}")
|
||||||
|
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"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func runStepRequest(r *SessionRunner, step *TStep) (stepResult *StepResult, err error) {
|
func runStepRequest(r *SessionRunner, step *TStep) (stepResult *StepResult, err error) {
|
||||||
stepResult = &StepResult{
|
stepResult = &StepResult{
|
||||||
Name: step.Name,
|
Name: step.Name,
|
||||||
@@ -271,6 +295,11 @@ func runStepRequest(r *SessionRunner, step *TStep) (stepResult *StepResult, err
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
err = prepareUpload(r.parser, step)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// override step variables
|
// override step variables
|
||||||
stepVariables, err := r.MergeStepVariables(step.Variables)
|
stepVariables, err := r.MergeStepVariables(step.Variables)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -791,6 +820,12 @@ func (s *StepRequestWithOptionalArgs) WithBody(body interface{}) *StepRequestWit
|
|||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithUpload sets HTTP request body for uploading file(s).
|
||||||
|
func (s *StepRequestWithOptionalArgs) WithUpload(upload map[string]interface{}) *StepRequestWithOptionalArgs {
|
||||||
|
s.step.Request.Upload = upload
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
// TeardownHook adds a teardown hook for current teststep.
|
// TeardownHook adds a teardown hook for current teststep.
|
||||||
func (s *StepRequestWithOptionalArgs) TeardownHook(hook string) *StepRequestWithOptionalArgs {
|
func (s *StepRequestWithOptionalArgs) TeardownHook(hook string) *StepRequestWithOptionalArgs {
|
||||||
s.step.TeardownHooks = append(s.step.TeardownHooks, hook)
|
s.step.TeardownHooks = append(s.step.TeardownHooks, hook)
|
||||||
|
|||||||
4
hrp/tests/test.env
Normal file
4
hrp/tests/test.env
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
UserName=test
|
||||||
|
Password=654321
|
||||||
|
PROJECT_KEY=AAABBBCCC
|
||||||
|
content_type=application/json; charset=UTF-8
|
||||||
38
hrp/tests/upload_test.go
Normal file
38
hrp/tests/upload_test.go
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
package tests
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/httprunner/httprunner/v4/hrp"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCaseUploadFile(t *testing.T) {
|
||||||
|
testcase := &hrp.TestCase{
|
||||||
|
Config: hrp.NewConfig("test upload file to httpbin").
|
||||||
|
SetBaseURL("https://httpbin.org"),
|
||||||
|
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"},
|
||||||
|
}).
|
||||||
|
POST("/post").
|
||||||
|
WithHeaders(map[string]string{"Content-Type": "${multipart_content_type($m_encoder)}"}).
|
||||||
|
WithBody("$m_encoder").
|
||||||
|
Validate().
|
||||||
|
AssertEqual("status_code", 200, "check status code").
|
||||||
|
AssertStartsWith("body.files.file", "UserName=test", "check uploaded file"),
|
||||||
|
hrp.NewStep("upload file with keyword").
|
||||||
|
POST("/post").
|
||||||
|
WithUpload(map[string]interface{}{"file": "test.env"}).
|
||||||
|
Validate().
|
||||||
|
AssertEqual("status_code", 200, "check status code").
|
||||||
|
AssertStartsWith("body.files.file", "UserName=test", "check uploaded file"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
err := hrp.NewRunner(t).Run(testcase)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("run testcase error: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user