Files
httprunner/hrp/runner.go
2022-04-16 00:35:25 +08:00

232 lines
6.0 KiB
Go

package hrp
import (
"crypto/tls"
"fmt"
"net"
"net/http"
"net/url"
"path/filepath"
"testing"
"time"
"github.com/pkg/errors"
"github.com/rs/zerolog/log"
"golang.org/x/net/http2"
"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,
httpClient: &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
},
Timeout: 30 * time.Second,
},
http2Client: &http.Client{
Transport: &http2.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
httpClient *http.Client
http2Client *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).
Bool("disableKeepAlive", disableKeepAlive).
Bool("disableCompression", disableCompression).
Msg("[init] SetClientTransport")
r.httpClient.Transport = &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
DialContext: (&net.Dialer{}).DialContext,
MaxIdleConns: 0,
MaxIdleConnsPerHost: maxConns,
DisableKeepAlives: disableKeepAlive,
DisableCompression: disableCompression,
}
r.http2Client.Transport = &http2.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
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.httpClient.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 {
sessionRunner, err := r.NewSessionRunner(testcase)
if err != nil {
log.Error().Err(err).Msg("[Run] init session runner failed")
return err
}
defer func() {
if sessionRunner.parser.plugin != nil {
sessionRunner.parser.plugin.Quit()
}
}()
for it := sessionRunner.parametersIterator; it.HasNext(); {
if err = sessionRunner.Start(it.Next()); 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()
// update the report output path
pluginPath, err := locatePlugin(testcases[0].GetPath())
if err == nil {
outputPath, _ := filepath.Split(pluginPath)
summaryPath = filepath.Join(outputPath, summaryPath)
reportPath = filepath.Join(outputPath, reportPath)
}
// 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
}
// NewSessionRunner creates a new session runner for testcase.
// each testcase has its own session runner
func (r *HRPRunner) NewSessionRunner(testcase *TestCase) (*SessionRunner, error) {
sessionRunner := &SessionRunner{
testCase: testcase,
hrpRunner: r,
parser: newParser(),
summary: newSummary(),
}
// init parser plugin
plugin, err := initPlugin(testcase.Config.Path, r.pluginLogOn)
if err != nil {
return nil, errors.Wrap(err, "init plugin failed")
}
sessionRunner.parser.plugin = plugin
// parse testcase config
if err := sessionRunner.parseConfig(); err != nil {
return nil, errors.Wrap(err, "parse testcase config failed")
}
return sessionRunner, nil
}