mirror of
https://github.com/httprunner/httprunner.git
synced 2026-05-12 11:29:48 +08:00
251 lines
5.9 KiB
Go
251 lines
5.9 KiB
Go
package hrp
|
|
|
|
import (
|
|
"net/http"
|
|
"os"
|
|
"testing"
|
|
|
|
"github.com/imroc/req"
|
|
"github.com/pkg/errors"
|
|
"github.com/rs/zerolog"
|
|
"github.com/rs/zerolog/log"
|
|
)
|
|
|
|
func init() {
|
|
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
|
|
}
|
|
|
|
// run API test with default configs
|
|
func Run(t *testing.T, testcases ...ITestCase) error {
|
|
return NewRunner().WithTestingT(t).SetDebug(true).Run(testcases...)
|
|
}
|
|
|
|
func NewRunner() *Runner {
|
|
return &Runner{
|
|
t: &testing.T{},
|
|
debug: false, // default to turn off debug
|
|
client: req.New(),
|
|
}
|
|
}
|
|
|
|
type Runner struct {
|
|
t *testing.T
|
|
debug bool
|
|
client *req.Req
|
|
}
|
|
|
|
func (r *Runner) WithTestingT(t *testing.T) *Runner {
|
|
log.Info().Msg("[init] WithTestingT")
|
|
r.t = t
|
|
return r
|
|
}
|
|
|
|
func (r *Runner) SetDebug(debug bool) *Runner {
|
|
log.Info().Bool("debug", debug).Msg("[init] SetDebug")
|
|
r.debug = debug
|
|
return r
|
|
}
|
|
|
|
func (r *Runner) SetProxyUrl(proxyUrl string) *Runner {
|
|
log.Info().Str("proxyUrl", proxyUrl).Msg("[init] SetProxyUrl")
|
|
r.client.SetProxyUrl(proxyUrl)
|
|
return r
|
|
}
|
|
|
|
func (r *Runner) Run(testcases ...ITestCase) error {
|
|
for _, iTestCase := range testcases {
|
|
testcase, err := iTestCase.ToTestCase()
|
|
if err != nil {
|
|
log.Error().Err(err).Msg("[Run] convert ITestCase interface to TestCase struct failed")
|
|
return err
|
|
}
|
|
if err := r.runCase(testcase); err != nil {
|
|
log.Error().Err(err).Msg("[Run] run testcase failed")
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (r *Runner) runCase(testcase *TestCase) error {
|
|
config := &testcase.Config
|
|
if err := r.parseConfig(config); err != nil {
|
|
return err
|
|
}
|
|
|
|
log.Info().Str("testcase", config.Name).Msg("run testcase start")
|
|
|
|
extractedVariables := make(map[string]interface{})
|
|
|
|
for _, step := range testcase.TestSteps {
|
|
// override variables
|
|
// step variables > extracted variables from previous steps
|
|
stepVariables := mergeVariables(step.ToStruct().Variables, extractedVariables)
|
|
// step variables > testcase config variables
|
|
stepVariables = mergeVariables(stepVariables, config.Variables)
|
|
|
|
// parse step variables
|
|
parsedVariables, err := parseVariables(stepVariables)
|
|
if err != nil {
|
|
log.Error().Interface("variables", config.Variables).Err(err).Msg("parse step variables failed")
|
|
return err
|
|
}
|
|
step.ToStruct().Variables = parsedVariables
|
|
|
|
stepData, err := r.runStep(step, config)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// update extracted variables
|
|
for k, v := range stepData.ExportVars {
|
|
extractedVariables[k] = v
|
|
}
|
|
}
|
|
|
|
log.Info().Str("testcase", config.Name).Msg("run testcase end")
|
|
return nil
|
|
}
|
|
|
|
func (r *Runner) runStep(step IStep, config *TConfig) (stepData *StepData, err error) {
|
|
log.Info().Str("step", step.Name()).Msg("run step start")
|
|
if tc, ok := step.(*testcaseWithOptionalArgs); ok {
|
|
// run referenced testcase
|
|
log.Info().Str("testcase", tc.step.Name).Msg("run referenced testcase")
|
|
// TODO: override testcase config
|
|
stepData, err = r.runStepTestCase(tc.step)
|
|
if err != nil {
|
|
return
|
|
}
|
|
} else {
|
|
// run request
|
|
tStep := parseStep(step, config)
|
|
stepData, err = r.runStepRequest(tStep)
|
|
if err != nil {
|
|
return
|
|
}
|
|
}
|
|
log.Info().
|
|
Str("step", step.Name()).
|
|
Bool("success", stepData.Success).
|
|
Interface("exportVars", stepData.ExportVars).
|
|
Msg("run step end")
|
|
return
|
|
}
|
|
|
|
func (r *Runner) runStepRequest(step *TStep) (stepData *StepData, err error) {
|
|
stepData = &StepData{
|
|
Name: step.Name,
|
|
Success: false,
|
|
ResponseLength: 0,
|
|
}
|
|
|
|
// prepare request args
|
|
var v []interface{}
|
|
if len(step.Request.Headers) > 0 {
|
|
headers, err := parseHeaders(step.Request.Headers, step.Variables)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
v = append(v, req.Header(headers))
|
|
}
|
|
if len(step.Request.Params) > 0 {
|
|
params, err := parseData(step.Request.Params, step.Variables)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
v = append(v, req.Param(params.(map[string]interface{})))
|
|
}
|
|
if step.Request.Body != nil {
|
|
data, err := parseData(step.Request.Body, step.Variables)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
switch data.(type) {
|
|
case map[string]interface{}: // post json
|
|
v = append(v, req.BodyJSON(data))
|
|
default: // post raw data
|
|
v = append(v, data)
|
|
}
|
|
}
|
|
|
|
for cookieName, cookieValue := range step.Request.Cookies {
|
|
v = append(v, &http.Cookie{
|
|
Name: cookieName,
|
|
Value: cookieValue,
|
|
})
|
|
}
|
|
|
|
// do request action
|
|
req.Debug = r.debug
|
|
resp, err := r.client.Do(string(step.Request.Method), step.Request.URL, v...)
|
|
if err != nil {
|
|
return
|
|
}
|
|
defer resp.Response().Body.Close()
|
|
|
|
// new response object
|
|
respObj, err := NewResponseObject(r.t, resp)
|
|
if err != nil {
|
|
err = errors.Wrap(err, "init ResponseObject error")
|
|
return
|
|
}
|
|
|
|
// extract variables from response
|
|
extractors := step.Extract
|
|
extractMapping := respObj.Extract(extractors)
|
|
stepData.ExportVars = extractMapping
|
|
|
|
// override step variables with extracted variables
|
|
stepVariables := mergeVariables(step.Variables, extractMapping)
|
|
|
|
// validate response
|
|
err = respObj.Validate(step.Validators, stepVariables)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
stepData.Success = true
|
|
stepData.ResponseLength = resp.Response().ContentLength
|
|
return
|
|
}
|
|
|
|
func (r *Runner) runStepTestCase(step *TStep) (stepData *StepData, err error) {
|
|
stepData = &StepData{
|
|
Name: step.Name,
|
|
Success: false,
|
|
}
|
|
testcase := step.TestCase
|
|
err = r.runCase(testcase)
|
|
return
|
|
}
|
|
|
|
func (r *Runner) parseConfig(config *TConfig) error {
|
|
// parse config variables
|
|
parsedVariables, err := parseVariables(config.Variables)
|
|
if err != nil {
|
|
log.Error().Interface("variables", config.Variables).Err(err).Msg("parse config variables failed")
|
|
return err
|
|
}
|
|
config.Variables = parsedVariables
|
|
|
|
// parse config name
|
|
parsedName, err := parseString(config.Name, config.Variables)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
config.Name = convertString(parsedName)
|
|
|
|
// parse config base url
|
|
parsedBaseURL, err := parseString(config.BaseURL, config.Variables)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
config.BaseURL = convertString(parsedBaseURL)
|
|
|
|
return nil
|
|
}
|
|
|
|
func (r *Runner) GetSummary() *TestCaseSummary {
|
|
return &TestCaseSummary{}
|
|
}
|