package hrp import ( "crypto/tls" "fmt" "net" "net/http" "net/url" "path/filepath" "testing" "time" "github.com/rs/zerolog/log" "github.com/httprunner/httprunner/hrp/internal/builtin" "github.com/httprunner/httprunner/hrp/internal/sdk" ) // Run starts to run API test with default configs. func Run(testcases ...ITestCase) error { t := &testing.T{} return NewRunner(t).SetRequestsLogOn().Run(testcases...) } // NewRunner constructs a new runner instance. func NewRunner(t *testing.T) *HRPRunner { if t == nil { t = &testing.T{} } return &HRPRunner{ t: t, failfast: true, // default to failfast genHTMLReport: false, client: &http.Client{ Transport: &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, }, Timeout: 30 * time.Second, }, } } type HRPRunner struct { t *testing.T failfast bool requestsLogOn bool pluginLogOn bool saveTests bool genHTMLReport bool client *http.Client } // SetClientTransport configures transport of http client for high concurrency load testing func (r *HRPRunner) SetClientTransport(maxConns int, disableKeepAlive bool, disableCompression bool) *HRPRunner { log.Info().Int("maxConns", maxConns).Msg("[init] SetClientTransport") r.client.Transport = &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, DialContext: (&net.Dialer{}).DialContext, MaxIdleConns: 0, MaxIdleConnsPerHost: maxConns, DisableKeepAlives: disableKeepAlive, DisableCompression: disableCompression, } return r } // SetFailfast configures whether to stop running when one step fails. func (r *HRPRunner) SetFailfast(failfast bool) *HRPRunner { log.Info().Bool("failfast", failfast).Msg("[init] SetFailfast") r.failfast = failfast return r } // SetRequestsLogOn turns on request & response details logging. func (r *HRPRunner) SetRequestsLogOn() *HRPRunner { log.Info().Msg("[init] SetRequestsLogOn") r.requestsLogOn = true return r } // SetPluginLogOn turns on plugin logging. func (r *HRPRunner) SetPluginLogOn() *HRPRunner { log.Info().Msg("[init] SetPluginLogOn") r.pluginLogOn = true return r } // SetProxyUrl configures the proxy URL, which is usually used to capture HTTP packets for debugging. func (r *HRPRunner) SetProxyUrl(proxyUrl string) *HRPRunner { log.Info().Str("proxyUrl", proxyUrl).Msg("[init] SetProxyUrl") p, err := url.Parse(proxyUrl) if err != nil { log.Error().Err(err).Str("proxyUrl", proxyUrl).Msg("[init] invalid proxyUrl") return r } r.client.Transport = &http.Transport{ Proxy: http.ProxyURL(p), TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, } return r } // SetSaveTests configures whether to save summary of tests. func (r *HRPRunner) SetSaveTests(saveTests bool) *HRPRunner { log.Info().Bool("saveTests", saveTests).Msg("[init] SetSaveTests") r.saveTests = saveTests return r } // GenHTMLReport configures whether to gen html report of api tests. func (r *HRPRunner) GenHTMLReport() *HRPRunner { log.Info().Bool("genHTMLReport", true).Msg("[init] SetgenHTMLReport") r.genHTMLReport = true return r } // Run starts to execute one or multiple testcases. func (r *HRPRunner) Run(testcases ...ITestCase) error { event := sdk.EventTracking{ Category: "RunAPITests", Action: "hrp run", } // report start event go sdk.SendEvent(event) // report execution timing event defer sdk.SendEvent(event.StartTiming("execution")) // record execution data to summary s := newOutSummary() // load all testcases testCases, err := loadTestCases(testcases...) if err != nil { return err } // run testcase one by one for _, testcase := range testCases { cfg := testcase.Config // parse config parameters err := initParameterIterator(cfg, "runner") if err != nil { log.Error().Interface("parameters", cfg.Parameters).Err(err).Msg("parse config parameters failed") return err } // 在runner模式下,指定整体策略,cfg.ParametersSetting.Iterators仅包含一个CartesianProduct的迭代器 for it := cfg.ParametersSetting.Iterators[0]; it.HasNext(); { // iterate through all parameter iterators and update case variables for _, it := range cfg.ParametersSetting.Iterators { if it.HasNext() { cfg.Variables = mergeVariables(it.Next(), cfg.Variables) } } sessionRunner := r.NewSessionRunner(testcase) if err = sessionRunner.Start(); err != nil { log.Error().Err(err).Msg("[Run] run testcase failed") return err } caseSummary := sessionRunner.GetSummary() s.appendCaseSummary(caseSummary) } } s.Time.Duration = time.Since(s.Time.StartAt).Seconds() // save summary if r.saveTests { dir, _ := filepath.Split(summaryPath) err := builtin.EnsureFolderExists(dir) if err != nil { return err } err = builtin.Dump2JSON(s, fmt.Sprintf(summaryPath, s.Time.StartAt.Unix())) if err != nil { return err } } // generate HTML report if r.genHTMLReport { err := s.genHTMLReport() if err != nil { return err } } return nil } func (r *HRPRunner) NewSessionRunner(testcase *TestCase) *SessionRunner { sessionRunner := &SessionRunner{ testCase: testcase, hrpRunner: r, parser: newParser(), summary: newSummary(), } sessionRunner.init() return sessionRunner }