refactor: move tests

This commit is contained in:
lilong.129
2025-03-05 11:45:59 +08:00
parent e107389d6e
commit b9db874f38
25 changed files with 617 additions and 604 deletions

View File

@@ -10,11 +10,11 @@ import (
"regexp"
"strings"
"github.com/httprunner/funplugin/fungo"
"github.com/httprunner/funplugin/myexec"
"github.com/pkg/errors"
"github.com/rs/zerolog/log"
"github.com/httprunner/funplugin/fungo"
"github.com/httprunner/funplugin/myexec"
"github.com/httprunner/httprunner/v5/code"
"github.com/httprunner/httprunner/v5/internal/builtin"
"github.com/httprunner/httprunner/v5/internal/config"

View File

@@ -1,141 +0,0 @@
package hrp
import (
"path/filepath"
"regexp"
"testing"
"github.com/stretchr/testify/assert"
)
func TestRun(t *testing.T) {
err := BuildPlugin(tmpl("plugin/debugtalk.go"), "./debugtalk.bin")
if !assert.Nil(t, err) {
t.Fatal()
}
genDebugTalkPyPath := filepath.Join(tmpl("plugin/"), PluginPySourceGenFile)
err = BuildPlugin(tmpl("plugin/debugtalk.py"), genDebugTalkPyPath)
if !assert.Nil(t, err) {
t.Fatal()
}
contentBytes, err := readFile(genDebugTalkPyPath)
if !assert.Nil(t, err) {
t.Fatal()
}
content := string(contentBytes)
if !assert.Contains(t, content, "import funppy") {
t.Fatal()
}
if !assert.Contains(t, content, "funppy.register") {
t.Fatal()
}
reg, _ := regexp.Compile(`funppy\.register`)
matchedSlice := reg.FindAllStringSubmatch(content, -1)
if !assert.Len(t, matchedSlice, 10) {
t.Fatal()
}
}
func TestFindAllPythonFunctionNames(t *testing.T) {
content := `
def test_1(): # exported function
pass
def _test_2(): # exported function
pass
def __test_3(): # private function
pass
# def test_4(): # commented out function
# pass
def Test5(): # exported function
pass
`
names, err := regexPyFunctionName.findAllFunctionNames(content)
if !assert.Nil(t, err) {
t.FailNow()
}
if !assert.Contains(t, names, "test_1") {
t.FailNow()
}
if !assert.Contains(t, names, "Test5") {
t.FailNow()
}
if !assert.Contains(t, names, "_test_2") {
t.FailNow()
}
if !assert.NotContains(t, names, "__test_3") {
t.FailNow()
}
// commented out function
if !assert.NotContains(t, names, "test_4") {
t.FailNow()
}
}
func TestFindAllGoFunctionNames(t *testing.T) {
content := `
func Test1() { // exported function
return
}
func testFunc2() { // exported function
return
}
func init() { // private function
return
}
func _Test3() { // exported function
return
}
// func Test4() { // commented out function
// return
// }
`
names, err := regexGoFunctionName.findAllFunctionNames(content)
if !assert.Nil(t, err) {
t.FailNow()
}
if !assert.Contains(t, names, "Test1") {
t.FailNow()
}
if !assert.Contains(t, names, "testFunc2") {
t.FailNow()
}
if !assert.NotContains(t, names, "init") {
t.FailNow()
}
if !assert.Contains(t, names, "_Test3") {
t.FailNow()
}
// commented out function
if !assert.NotContains(t, names, "Test4") {
t.FailNow()
}
}
func TestFindAllGoFunctionNamesAbnormal(t *testing.T) {
content := `
func init() { // private function
return
}
func main() { // should not define main() function
return
}
`
_, err := regexGoFunctionName.findAllFunctionNames(content)
if !assert.NotNil(t, err) {
t.FailNow()
}
}

View File

@@ -80,7 +80,7 @@ func (c *TConfig) WithParameters(parameters map[string]interface{}) *TConfig {
}
// SetThinkTime sets think time config for current testcase.
func (c *TConfig) SetThinkTime(strategy thinkTimeStrategy, cfg interface{}, limit float64) *TConfig {
func (c *TConfig) SetThinkTime(strategy ThinkTimeStrategy, cfg interface{}, limit float64) *TConfig {
c.ThinkTimeSetting = &ThinkTimeConfig{strategy, cfg, limit}
return c
}
@@ -194,7 +194,7 @@ func (c *TConfig) DisableAutoPopupHandler() *TConfig {
}
type ThinkTimeConfig struct {
Strategy thinkTimeStrategy `json:"strategy,omitempty" yaml:"strategy,omitempty"` // default、random、multiply、ignore
Strategy ThinkTimeStrategy `json:"strategy,omitempty" yaml:"strategy,omitempty"` // default、random、multiply、ignore
Setting interface{} `json:"setting,omitempty" yaml:"setting,omitempty"` // random(map): {"min_percentage": 0.5, "max_percentage": 1.5}; 10、multiply(float64): 1.5
Limit float64 `json:"limit,omitempty" yaml:"limit,omitempty"` // limit think time no more than specific time, ignore if value <= 0
}
@@ -205,10 +205,10 @@ func (ttc *ThinkTimeConfig) checkThinkTime() {
}
// unset strategy, set default strategy
if ttc.Strategy == "" {
ttc.Strategy = thinkTimeDefault
ttc.Strategy = ThinkTimeDefault
}
// check think time
if ttc.Strategy == thinkTimeRandomPercentage {
if ttc.Strategy == ThinkTimeRandomPercentage {
if ttc.Setting == nil || reflect.TypeOf(ttc.Setting).Kind() != reflect.Map {
ttc.Setting = thinkTimeDefaultRandom
return
@@ -237,7 +237,7 @@ func (ttc *ThinkTimeConfig) checkThinkTime() {
return
}
ttc.Setting = map[string]float64{"min_percentage": left, "max_percentage": right}
} else if ttc.Strategy == thinkTimeMultiply {
} else if ttc.Strategy == ThinkTimeMultiply {
if ttc.Setting == nil {
ttc.Setting = float64(0) // default
return
@@ -248,19 +248,19 @@ func (ttc *ThinkTimeConfig) checkThinkTime() {
return
}
ttc.Setting = value
} else if ttc.Strategy != thinkTimeIgnore {
} else if ttc.Strategy != ThinkTimeIgnore {
// unrecognized strategy, set default strategy
ttc.Strategy = thinkTimeDefault
ttc.Strategy = ThinkTimeDefault
}
}
type thinkTimeStrategy string
type ThinkTimeStrategy string
const (
thinkTimeDefault thinkTimeStrategy = "default" // as recorded
thinkTimeRandomPercentage thinkTimeStrategy = "random_percentage" // use random percentage of recorded think time
thinkTimeMultiply thinkTimeStrategy = "multiply" // multiply recorded think time
thinkTimeIgnore thinkTimeStrategy = "ignore" // ignore recorded think time
ThinkTimeDefault ThinkTimeStrategy = "default" // as recorded
ThinkTimeRandomPercentage ThinkTimeStrategy = "random_percentage" // use random percentage of recorded think time
ThinkTimeMultiply ThinkTimeStrategy = "multiply" // multiply recorded think time
ThinkTimeIgnore ThinkTimeStrategy = "ignore" // ignore recorded think time
)
const (

View File

@@ -225,6 +225,22 @@ func InterfaceType(raw interface{}) string {
return reflect.TypeOf(raw).String()
}
func LoadFile(path string) ([]byte, error) {
var err error
path, err = filepath.Abs(path)
if err != nil {
log.Error().Err(err).Str("path", path).Msg("convert absolute path failed")
return nil, errors.Wrap(code.LoadFileError, err.Error())
}
file, err := os.ReadFile(path)
if err != nil {
log.Error().Err(err).Msg("read file failed")
return nil, errors.Wrap(code.LoadFileError, err.Error())
}
return file, nil
}
func loadFromCSV(path string) []map[string]interface{} {
log.Info().Str("path", path).Msg("load csv file")
file, err := os.ReadFile(path)

View File

@@ -1 +1 @@
v5.0.0+2503051104
v5.0.0+2503051147

View File

@@ -12,6 +12,7 @@ import (
"gopkg.in/yaml.v2"
"github.com/httprunner/httprunner/v5/code"
"github.com/httprunner/httprunner/v5/internal/builtin"
"github.com/httprunner/httprunner/v5/internal/json"
)
@@ -85,7 +86,7 @@ func LoadTestCases(tests ...ITestCase) ([]*TestCase, error) {
// LoadFileObject loads file content with file extension and assigns to structObj
func LoadFileObject(path string, structObj interface{}) (err error) {
log.Info().Str("path", path).Msg("load file")
file, err := readFile(path)
file, err := builtin.LoadFile(path)
if err != nil {
return errors.Wrap(err, "read file failed")
}
@@ -145,19 +146,3 @@ func parseEnvContent(file []byte, obj interface{}) error {
}
return nil
}
func readFile(path string) ([]byte, error) {
var err error
path, err = filepath.Abs(path)
if err != nil {
log.Error().Err(err).Str("path", path).Msg("convert absolute path failed")
return nil, errors.Wrap(code.LoadFileError, err.Error())
}
file, err := os.ReadFile(path)
if err != nil {
log.Error().Err(err).Msg("read file failed")
return nil, errors.Wrap(code.LoadFileError, err.Error())
}
return file, nil
}

View File

@@ -13,7 +13,7 @@ import (
type TParamsConfig struct {
PickOrder iteratorPickOrder `json:"pick_order,omitempty" yaml:"pick_order,omitempty"` // overall pick-order strategy
Strategies map[string]iteratorStrategy `json:"strategies,omitempty" yaml:"strategies,omitempty"` // individual strategies for each parameters
Strategies map[string]IteratorStrategy `json:"strategies,omitempty" yaml:"strategies,omitempty"` // individual strategies for each parameters
Limit int `json:"limit,omitempty" yaml:"limit,omitempty"`
}
@@ -35,13 +35,13 @@ const (
*/
type Parameters []map[string]interface{}
type iteratorStrategy struct {
type IteratorStrategy struct {
Name string `json:"name,omitempty" yaml:"name,omitempty"`
PickOrder iteratorPickOrder `json:"pick_order,omitempty" yaml:"pick_order,omitempty"`
}
func (p *Parser) initParametersIterator(cfg *TConfig) (*ParametersIterator, error) {
parameters, err := p.loadParameters(cfg.Parameters, cfg.Variables)
func (p *Parser) InitParametersIterator(cfg *TConfig) (*ParametersIterator, error) {
parameters, err := p.LoadParameters(cfg.Parameters, cfg.Variables)
if err != nil {
return nil, err
}
@@ -57,13 +57,13 @@ func newParametersIterator(parameters map[string]Parameters, config *TParamsConf
hasNext: true,
sequentialParameters: nil,
randomParameterNames: nil,
limit: config.Limit,
index: 0,
Limit: config.Limit,
Index: 0,
}
if len(parameters) == 0 {
iterator.data = map[string]Parameters{}
iterator.limit = 1
iterator.Limit = 1
return iterator
}
@@ -85,24 +85,24 @@ func newParametersIterator(parameters map[string]Parameters, config *TParamsConf
}
// generate cartesian product for sequential parameters
iterator.sequentialParameters = genCartesianProduct(parametersList)
iterator.sequentialParameters = GenCartesianProduct(parametersList)
if iterator.limit < 0 {
if iterator.Limit < 0 {
log.Warn().Msg("parameters unlimited mode is only supported for load testing")
iterator.limit = 0
iterator.Limit = 0
}
if iterator.limit == 0 {
if iterator.Limit == 0 {
// limit not set
if len(iterator.sequentialParameters) > 0 {
// use cartesian product of sequential parameters size as limit
iterator.limit = len(iterator.sequentialParameters)
iterator.Limit = len(iterator.sequentialParameters)
} else {
// all parameters are selected by random
// only run once
iterator.limit = 1
iterator.Limit = 1
}
} else { // limit > 0
log.Info().Int("limit", iterator.limit).Msg("set limit for parameters")
log.Info().Int("limit", iterator.Limit).Msg("set limit for parameters")
}
return iterator
@@ -114,14 +114,14 @@ type ParametersIterator struct {
hasNext bool // cache query result
sequentialParameters Parameters // cartesian product for sequential parameters
randomParameterNames []string // value is parameter names
limit int // limit count for iteration
index int // current iteration index
Limit int // limit count for iteration
Index int // current iteration index
}
// SetUnlimitedMode is used for load testing
func (iter *ParametersIterator) SetUnlimitedMode() {
log.Info().Msg("set parameters unlimited mode")
iter.limit = -1
iter.Limit = -1
}
func (iter *ParametersIterator) HasNext() bool {
@@ -130,12 +130,12 @@ func (iter *ParametersIterator) HasNext() bool {
}
// unlimited mode
if iter.limit == -1 {
if iter.Limit == -1 {
return true
}
// reached limit
if iter.index >= iter.limit {
if iter.Index >= iter.Limit {
// cache query result
iter.hasNext = false
return false
@@ -155,11 +155,11 @@ func (iter *ParametersIterator) Next() map[string]interface{} {
var selectedParameters map[string]interface{}
if len(iter.sequentialParameters) == 0 {
selectedParameters = make(map[string]interface{})
} else if iter.index < len(iter.sequentialParameters) {
selectedParameters = iter.sequentialParameters[iter.index]
} else if iter.Index < len(iter.sequentialParameters) {
selectedParameters = iter.sequentialParameters[iter.Index]
} else {
// loop back to the first sequential parameter
index := iter.index % len(iter.sequentialParameters)
index := iter.Index % len(iter.sequentialParameters)
selectedParameters = iter.sequentialParameters[index]
}
@@ -172,8 +172,8 @@ func (iter *ParametersIterator) Next() map[string]interface{} {
}
}
iter.index++
if iter.limit > 0 && iter.index >= iter.limit {
iter.Index++
if iter.Limit > 0 && iter.Index >= iter.Limit {
iter.hasNext = false
}
@@ -188,7 +188,7 @@ func (iter *ParametersIterator) Data() map[string]interface{} {
return res
}
func genCartesianProduct(multiParameters []Parameters) Parameters {
func GenCartesianProduct(multiParameters []Parameters) Parameters {
if len(multiParameters) == 0 {
return nil
}
@@ -208,7 +208,7 @@ func genCartesianProduct(multiParameters []Parameters) Parameters {
}
/*
loadParameters loads parameters from multiple sources.
LoadParameters loads parameters from multiple sources.
parameter value may be in three types:
@@ -240,7 +240,7 @@ parameter value may be in three types:
]
}
*/
func (p *Parser) loadParameters(configParameters map[string]interface{}, variablesMapping map[string]interface{}) (
func (p *Parser) LoadParameters(configParameters map[string]interface{}, variablesMapping map[string]interface{}) (
map[string]Parameters, error) {
if len(configParameters) == 0 {
@@ -291,7 +291,7 @@ func (p *Parser) loadParameters(configParameters map[string]interface{}, variabl
return nil, errors.New("config parameters raw value format error")
}
parameterSlice, err := convertParameters(k, parametersRawList)
parameterSlice, err := ConvertParameters(k, parametersRawList)
if err != nil {
return nil, err
}
@@ -320,7 +320,7 @@ case 3:
key = "username-password"
parametersRawList = [["test1", "111111"], ["test2", "222222"]]
*/
func convertParameters(key string, parametersRawList interface{}) (parameterSlice []map[string]interface{}, err error) {
func ConvertParameters(key string, parametersRawList interface{}) (parameterSlice []map[string]interface{}, err error) {
parametersRawSlice := reflect.ValueOf(parametersRawList)
if parametersRawSlice.Kind() != reflect.Slice {
return nil, errors.New("parameters raw value is not list")

View File

@@ -10,17 +10,17 @@ import (
"strconv"
"strings"
"github.com/httprunner/funplugin"
"github.com/httprunner/funplugin/fungo"
"github.com/maja42/goval"
"github.com/pkg/errors"
"github.com/rs/zerolog/log"
"github.com/httprunner/funplugin"
"github.com/httprunner/funplugin/fungo"
"github.com/httprunner/httprunner/v5/code"
"github.com/httprunner/httprunner/v5/internal/builtin"
)
func newParser() *Parser {
func NewParser() *Parser {
return &Parser{}
}

View File

@@ -1,8 +1,11 @@
package hrp
import (
"io"
"net/http"
"net/url"
"sort"
"strings"
"testing"
"time"
@@ -208,7 +211,7 @@ func TestParseDataStringWithVariables(t *testing.T) {
{"abc$var_5", "abctrue"}, // "abcTrue"
}
parser := newParser()
parser := NewParser()
for _, data := range testData {
parsedData, err := parser.Parse(data.expr, variablesMapping)
if !assert.NoError(t, err) {
@@ -233,7 +236,7 @@ func TestParseDataStringWithUndefinedVariables(t *testing.T) {
{"/api/$SECRET_KEY", "/api/$SECRET_KEY"}, // raise error
}
parser := newParser()
parser := NewParser()
for _, data := range testData {
parsedData, err := parser.Parse(data.expr, variablesMapping)
if !assert.Error(t, err) {
@@ -278,7 +281,7 @@ func TestParseDataStringWithVariablesAbnormal(t *testing.T) {
{"ABC$var_1{}a", "ABCabc{}a"}, // {}
}
parser := newParser()
parser := NewParser()
for _, data := range testData {
parsedData, err := parser.Parse(data.expr, variablesMapping)
if !assert.NoError(t, err) {
@@ -309,7 +312,7 @@ func TestParseDataMapWithVariables(t *testing.T) {
{map[string]interface{}{"$var2": "$val1"}, map[string]interface{}{"123": 200}},
}
parser := newParser()
parser := NewParser()
for _, data := range testData {
parsedData, err := parser.Parse(data.expr, variablesMapping)
if !assert.NoError(t, err) {
@@ -343,7 +346,7 @@ func TestParseHeaders(t *testing.T) {
{map[string]string{"$var2": "$val2"}, map[string]string{"123": "<nil>"}},
}
parser := newParser()
parser := NewParser()
for _, data := range testData {
parsedHeaders, err := parser.ParseHeaders(data.rawHeaders, variablesMapping)
if !assert.NoError(t, err) {
@@ -488,7 +491,7 @@ func TestMergeValidators(t *testing.T) {
}
func TestCallBuiltinFunction(t *testing.T) {
parser := newParser()
parser := NewParser()
// call function without arguments
_, err := parser.callFunc("get_timestamp")
@@ -601,7 +604,7 @@ func TestParseDataStringWithFunctions(t *testing.T) {
{"123${gen_random_string($n)}abc", 11},
}
parser := newParser()
parser := NewParser()
for _, data := range testData1 {
value, err := parser.Parse(data.expr, variablesMapping)
if !assert.NoError(t, err) {
@@ -670,7 +673,7 @@ func TestParseVariables(t *testing.T) {
},
}
parser := newParser()
parser := NewParser()
for _, data := range testData {
value, err := parser.ParseVariables(data.rawVars)
if !assert.NoError(t, err) {
@@ -701,7 +704,7 @@ func TestParseVariablesAbnormal(t *testing.T) {
},
}
parser := newParser()
parser := NewParser()
for _, data := range testData {
value, err := parser.ParseVariables(data.rawVars)
if !assert.Error(t, err) {
@@ -784,3 +787,193 @@ func TestFindallVariables(t *testing.T) {
}
}
}
func TestSearchJmespath(t *testing.T) {
testText := `{"a": {"b": "foo"}, "c": "bar", "d": {"e": [{"f": "foo"}, {"f": "bar"}]}}`
testData := []struct {
raw string
expected string
}{
{"body.a.b", "foo"},
{"body.c", "bar"},
{"body.d.e[0].f", "foo"},
{"body.d.e[1].f", "bar"},
}
resp := http.Response{}
resp.Body = io.NopCloser(strings.NewReader(testText))
respObj, err := newHttpResponseObject(t, NewParser(), &resp)
if err != nil {
t.Fatal()
}
for _, data := range testData {
if !assert.Equal(t, data.expected, respObj.searchJmespath(data.raw)) {
t.Fatal()
}
}
}
func TestSearchRegexp(t *testing.T) {
testText := `
<ul class="nav navbar-nav navbar-right">
<li><a href="/order/addToCart" style="color: white"><i class="fa fa-shopping-cart fa-2x"></i><span class="badge">0</span></a></li>
<li class="dropdown">
<a class="dropdown-toggle" data-toggle="dropdown" href="#" style="color: white">
Leo <i class="fa fa-cog fa-2x"></i><span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="/user/changePassword">Change Password</a></li>
<li><a href="/user/addAddress">Shipping</a></li>
<li><a href="/user/addCard">Payment</a></li>
<li><a href="/order/orderHistory">Order History</a></li>
<li><a href="/user/signOut">Sign Out</a></li>
</ul>
</li>
<li>&nbsp;&nbsp;&nbsp;</li>
<li><a href="/user/signOut" style="color: white"><i class="fa fa-sign-out fa-2x"></i>
Sign Out</a></li>
</ul>
`
testData := []struct {
raw string
expected string
}{
{"/user/signOut\">(.*)</a></li>", "Sign Out"},
{"<li><a href=\"/user/(.*)\" style", "signOut"},
{" (.*) <i class=\"fa fa-cog fa-2x\"></i>", "Leo"},
}
// new response object
resp := http.Response{}
resp.Body = io.NopCloser(strings.NewReader(testText))
respObj, err := newHttpResponseObject(t, NewParser(), &resp)
if err != nil {
t.Fatal()
}
for _, data := range testData {
if !assert.Equal(t, data.expected, respObj.searchRegexp(data.raw)) {
t.Fatal()
}
}
}
func TestConvertCheckExpr(t *testing.T) {
exprs := []struct {
before string
after string
}{
// normal check expression
{"a.b.c", "a.b.c"},
{"a.\"b-c\".d", "a.\"b-c\".d"},
{"a.b-c.d", "a.b-c.d"},
{"body.args.a[-1]", "body.args.a[-1]"},
// check expression using regex
{"covering (.*) testing,", "covering (.*) testing,"},
{" (.*) a-b-c", " (.*) a-b-c"},
// abnormal check expression
{"headers.Content-Type", "headers.\"Content-Type\""},
{"headers.\"Content-Type", "headers.\"Content-Type\""},
{"headers.Content-Type\"", "headers.\"Content-Type\""},
{"headers.User-Agent", "headers.\"User-Agent\""},
}
for _, expr := range exprs {
assert.Equal(t, expr.after, convertJmespathExpr(expr.before))
}
}
func TestFindAllPythonFunctionNames(t *testing.T) {
content := `
def test_1(): # exported function
pass
def _test_2(): # exported function
pass
def __test_3(): # private function
pass
# def test_4(): # commented out function
# pass
def Test5(): # exported function
pass
`
names, err := regexPyFunctionName.findAllFunctionNames(content)
if !assert.Nil(t, err) {
t.FailNow()
}
if !assert.Contains(t, names, "test_1") {
t.FailNow()
}
if !assert.Contains(t, names, "Test5") {
t.FailNow()
}
if !assert.Contains(t, names, "_test_2") {
t.FailNow()
}
if !assert.NotContains(t, names, "__test_3") {
t.FailNow()
}
// commented out function
if !assert.NotContains(t, names, "test_4") {
t.FailNow()
}
}
func TestFindAllGoFunctionNames(t *testing.T) {
content := `
func Test1() { // exported function
return
}
func testFunc2() { // exported function
return
}
func init() { // private function
return
}
func _Test3() { // exported function
return
}
// func Test4() { // commented out function
// return
// }
`
names, err := regexGoFunctionName.findAllFunctionNames(content)
if !assert.Nil(t, err) {
t.FailNow()
}
if !assert.Contains(t, names, "Test1") {
t.FailNow()
}
if !assert.Contains(t, names, "testFunc2") {
t.FailNow()
}
if !assert.NotContains(t, names, "init") {
t.FailNow()
}
if !assert.Contains(t, names, "_Test3") {
t.FailNow()
}
// commented out function
if !assert.NotContains(t, names, "Test4") {
t.FailNow()
}
}
func TestFindAllGoFunctionNamesAbnormal(t *testing.T) {
content := `
func init() { // private function
return
}
func main() { // should not define main() function
return
}
`
_, err := regexGoFunctionName.findAllFunctionNames(content)
if !assert.NotNil(t, err) {
t.FailNow()
}
}

View File

@@ -6,11 +6,11 @@ import (
"strings"
"sync"
"github.com/httprunner/funplugin"
"github.com/httprunner/funplugin/myexec"
"github.com/pkg/errors"
"github.com/rs/zerolog/log"
"github.com/httprunner/funplugin"
"github.com/httprunner/funplugin/myexec"
"github.com/httprunner/httprunner/v5/code"
"github.com/httprunner/httprunner/v5/internal/config"
"github.com/httprunner/httprunner/v5/internal/sdk"
@@ -39,7 +39,7 @@ func initPlugin(path, venv string, logOn bool) (plugin funplugin.IPlugin, err er
if path == "" {
return nil, nil
}
pluginPath, err := locatePlugin(path)
pluginPath, err := LocatePlugin(path)
if err != nil {
log.Warn().Str("path", path).Msg("locate plugin failed")
return nil, nil
@@ -100,21 +100,21 @@ func initPlugin(path, venv string, logOn bool) (plugin funplugin.IPlugin, err er
return
}
func locatePlugin(path string) (pluginPath string, err error) {
func LocatePlugin(path string) (pluginPath string, err error) {
log.Info().Str("path", path).Msg("locate plugin")
// priority: hashicorp plugin (debugtalk.bin > debugtalk.py) > go plugin (debugtalk.so)
pluginPath, err = locateFile(path, PluginHashicorpGoBuiltFile)
pluginPath, err = LocateFile(path, PluginHashicorpGoBuiltFile)
if err == nil {
return
}
pluginPath, err = locateFile(path, PluginPySourceFile)
pluginPath, err = LocateFile(path, PluginPySourceFile)
if err == nil {
return
}
pluginPath, err = locateFile(path, PluginGoBuiltFile)
pluginPath, err = LocateFile(path, PluginGoBuiltFile)
if err == nil {
return
}
@@ -122,9 +122,9 @@ func locatePlugin(path string) (pluginPath string, err error) {
return "", errors.New("plugin file not found")
}
// locateFile searches destFile upward recursively until system root dir
// LocateFile searches destFile upward recursively until system root dir
// if not found, then searches in hrp executable dir
func locateFile(startPath string, destFile string) (pluginPath string, err error) {
func LocateFile(startPath string, destFile string) (pluginPath string, err error) {
stat, err := os.Stat(startPath)
if os.IsNotExist(err) {
return "", errors.Wrap(err, "start path not exists")
@@ -153,7 +153,7 @@ func locateFile(startPath string, destFile string) (pluginPath string, err error
return "", errors.New("searched to system root dir, plugin file not found")
}
return locateFile(parentDir, destFile)
return LocateFile(parentDir, destFile)
}
// locateExecutable finds destFile in hrp executable dir
@@ -173,13 +173,13 @@ func locateExecutable(destFile string) (string, error) {
}
func GetProjectRootDirPath(path string) (rootDir string, err error) {
pluginPath, err := locatePlugin(path)
pluginPath, err := LocatePlugin(path)
if err == nil {
rootDir = filepath.Dir(pluginPath)
return
}
// fix: no debugtalk file in project but having proj.json created by startproject
projPath, err := locateFile(path, projectInfoFile)
projPath, err := LocateFile(path, projectInfoFile)
if err == nil {
rootDir = filepath.Dir(projPath)
return

View File

@@ -17,13 +17,14 @@ import (
"time"
"github.com/gorilla/websocket"
"github.com/httprunner/funplugin"
"github.com/jinzhu/copier"
"github.com/pkg/errors"
"github.com/rs/zerolog/log"
"golang.org/x/net/http2"
"github.com/httprunner/funplugin"
"github.com/httprunner/httprunner/v5/code"
"github.com/httprunner/httprunner/v5/internal/builtin"
"github.com/httprunner/httprunner/v5/internal/sdk"
"github.com/httprunner/httprunner/v5/internal/version"
"github.com/httprunner/httprunner/v5/uixt"
@@ -281,7 +282,7 @@ func (r *HRPRunner) NewCaseRunner(testcase TestCase) (*CaseRunner, error) {
caseRunner := &CaseRunner{
TestCase: testcase,
hrpRunner: r,
parser: newParser(),
parser: NewParser(),
uixtDrivers: make(map[string]*uixt.XTDriver),
}
config := testcase.Config.Get()
@@ -296,7 +297,7 @@ func (r *HRPRunner) NewCaseRunner(testcase TestCase) (*CaseRunner, error) {
// load plugin info to testcase config
pluginPath := plugin.Path()
pluginContent, err := readFile(pluginPath)
pluginContent, err := builtin.LoadFile(pluginPath)
if err != nil {
return nil, err
}
@@ -407,7 +408,7 @@ func (r *CaseRunner) parseConfig() (parsedConfig *TConfig, err error) {
parsedConfig.WebSocketSetting.checkWebSocket()
// parse testcase config parameters
parametersIterator, err := r.parser.initParametersIterator(parsedConfig)
parametersIterator, err := r.parser.InitParametersIterator(parsedConfig)
if err != nil {
log.Error().Err(err).
Interface("parameters", parsedConfig.Parameters).

View File

@@ -5,9 +5,10 @@ import (
"os"
"time"
"github.com/httprunner/httprunner/v5/uixt"
"github.com/pkg/errors"
"github.com/rs/zerolog/log"
"github.com/httprunner/httprunner/v5/uixt"
)
// StepFunction implements IStep interface.

View File

@@ -30,13 +30,13 @@ import (
type HTTPMethod string
const (
httpGET HTTPMethod = "GET"
httpHEAD HTTPMethod = "HEAD"
httpPOST HTTPMethod = "POST"
httpPUT HTTPMethod = "PUT"
httpDELETE HTTPMethod = "DELETE"
httpOPTIONS HTTPMethod = "OPTIONS"
httpPATCH HTTPMethod = "PATCH"
HTTP_GET HTTPMethod = "GET"
HTTP_HEAD HTTPMethod = "HEAD"
HTTP_POST HTTPMethod = "POST"
HTTP_PUT HTTPMethod = "PUT"
HTTP_DELETE HTTPMethod = "DELETE"
HTTP_OPTIONS HTTPMethod = "OPTIONS"
HTTP_PATCH HTTPMethod = "PATCH"
)
// Request represents HTTP request data structure.
@@ -566,11 +566,11 @@ func (s *StepRequest) Loop(times int) *StepRequest {
// GET makes a HTTP GET request.
func (s *StepRequest) GET(url string) *StepRequestWithOptionalArgs {
if s.Request != nil {
s.Request.Method = httpGET
s.Request.Method = HTTP_GET
s.Request.URL = url
} else {
s.Request = &Request{
Method: httpGET,
Method: HTTP_GET,
URL: url,
}
}
@@ -582,11 +582,11 @@ func (s *StepRequest) GET(url string) *StepRequestWithOptionalArgs {
// HEAD makes a HTTP HEAD request.
func (s *StepRequest) HEAD(url string) *StepRequestWithOptionalArgs {
if s.Request != nil {
s.Request.Method = httpHEAD
s.Request.Method = HTTP_HEAD
s.Request.URL = url
} else {
s.Request = &Request{
Method: httpHEAD,
Method: HTTP_HEAD,
URL: url,
}
}
@@ -598,11 +598,11 @@ func (s *StepRequest) HEAD(url string) *StepRequestWithOptionalArgs {
// POST makes a HTTP POST request.
func (s *StepRequest) POST(url string) *StepRequestWithOptionalArgs {
if s.Request != nil {
s.Request.Method = httpPOST
s.Request.Method = HTTP_POST
s.Request.URL = url
} else {
s.Request = &Request{
Method: httpPOST,
Method: HTTP_POST,
URL: url,
}
}
@@ -614,11 +614,11 @@ func (s *StepRequest) POST(url string) *StepRequestWithOptionalArgs {
// PUT makes a HTTP PUT request.
func (s *StepRequest) PUT(url string) *StepRequestWithOptionalArgs {
if s.Request != nil {
s.Request.Method = httpPUT
s.Request.Method = HTTP_PUT
s.Request.URL = url
} else {
s.Request = &Request{
Method: httpPUT,
Method: HTTP_PUT,
URL: url,
}
}
@@ -630,11 +630,11 @@ func (s *StepRequest) PUT(url string) *StepRequestWithOptionalArgs {
// DELETE makes a HTTP DELETE request.
func (s *StepRequest) DELETE(url string) *StepRequestWithOptionalArgs {
if s.Request != nil {
s.Request.Method = httpDELETE
s.Request.Method = HTTP_DELETE
s.Request.URL = url
} else {
s.Request = &Request{
Method: httpDELETE,
Method: HTTP_DELETE,
URL: url,
}
}
@@ -646,11 +646,11 @@ func (s *StepRequest) DELETE(url string) *StepRequestWithOptionalArgs {
// OPTIONS makes a HTTP OPTIONS request.
func (s *StepRequest) OPTIONS(url string) *StepRequestWithOptionalArgs {
if s.Request != nil {
s.Request.Method = httpOPTIONS
s.Request.Method = HTTP_OPTIONS
s.Request.URL = url
} else {
s.Request = &Request{
Method: httpOPTIONS,
Method: HTTP_OPTIONS,
URL: url,
}
}
@@ -662,11 +662,11 @@ func (s *StepRequest) OPTIONS(url string) *StepRequestWithOptionalArgs {
// PATCH makes a HTTP PATCH request.
func (s *StepRequest) PATCH(url string) *StepRequestWithOptionalArgs {
if s.Request != nil {
s.Request.Method = httpPATCH
s.Request.Method = HTTP_PATCH
s.Request.URL = url
} else {
s.Request = &Request{
Method: httpPATCH,
Method: HTTP_PATCH,
URL: url,
}
}

View File

@@ -1 +0,0 @@
package hrp

View File

@@ -42,14 +42,14 @@ func (s *StepThinkTime) Run(r *SessionRunner) (*StepResult, error) {
cfg := r.caseRunner.Config.Get().ThinkTimeSetting
if cfg == nil {
cfg = &ThinkTimeConfig{thinkTimeDefault, nil, 0}
cfg = &ThinkTimeConfig{ThinkTimeDefault, nil, 0}
}
var tt time.Duration
switch cfg.Strategy {
case thinkTimeDefault:
case ThinkTimeDefault:
tt = time.Duration(thinkTime.Time*1000) * time.Millisecond
case thinkTimeRandomPercentage:
case ThinkTimeRandomPercentage:
// e.g. {"min_percentage": 0.5, "max_percentage": 1.5}
m, ok := cfg.Setting.(map[string]float64)
if !ok {
@@ -58,13 +58,13 @@ func (s *StepThinkTime) Run(r *SessionRunner) (*StepResult, error) {
}
res := builtin.GetRandomNumber(int(thinkTime.Time*m["min_percentage"]*1000), int(thinkTime.Time*m["max_percentage"]*1000))
tt = time.Duration(res) * time.Millisecond
case thinkTimeMultiply:
case ThinkTimeMultiply:
value, ok := cfg.Setting.(float64) // e.g. 0.5
if !ok || value <= 0 {
value = thinkTimeDefaultMultiply
}
tt = time.Duration(thinkTime.Time*value*1000) * time.Millisecond
case thinkTimeIgnore:
case ThinkTimeIgnore:
// nothing to do
}

45
tests/build_test.go Normal file
View File

@@ -0,0 +1,45 @@
package tests
import (
"path/filepath"
"regexp"
"testing"
"github.com/stretchr/testify/assert"
hrp "github.com/httprunner/httprunner/v5"
"github.com/httprunner/httprunner/v5/internal/builtin"
)
func TestRun(t *testing.T) {
err := hrp.BuildPlugin(tmpl("plugin/debugtalk.go"), "./debugtalk.bin")
if !assert.Nil(t, err) {
t.Fatal()
}
genDebugTalkPyPath := filepath.Join(tmpl("plugin/"), hrp.PluginPySourceGenFile)
err = hrp.BuildPlugin(tmpl("plugin/debugtalk.py"), genDebugTalkPyPath)
if !assert.Nil(t, err) {
t.Fatal()
}
contentBytes, err := builtin.LoadFile(genDebugTalkPyPath)
if !assert.Nil(t, err) {
t.Fatal()
}
content := string(contentBytes)
if !assert.Contains(t, content, "import funppy") {
t.Fatal()
}
if !assert.Contains(t, content, "funppy.register") {
t.Fatal()
}
reg, _ := regexp.Compile(`funppy\.register`)
matchedSlice := reg.FindAllStringSubmatch(content, -1)
if !assert.Len(t, matchedSlice, 10) {
t.Fatal()
}
}

View File

@@ -1,15 +1,17 @@
package hrp
package tests
import (
"testing"
"github.com/stretchr/testify/assert"
hrp "github.com/httprunner/httprunner/v5"
)
func TestLoadTestCases(t *testing.T) {
// load test cases from folder path
tc := TestCasePath("../examples/demo-with-py-plugin/testcases/")
testCases, err := LoadTestCases(&tc)
tc := hrp.TestCasePath("../examples/demo-with-py-plugin/testcases/")
testCases, err := hrp.LoadTestCases(&tc)
if !assert.Nil(t, err) {
t.Fatal()
}
@@ -18,8 +20,8 @@ func TestLoadTestCases(t *testing.T) {
}
// load test cases from folder path, including sub folders
tc = TestCasePath("../examples/demo-with-py-plugin/")
testCases, err = LoadTestCases(&tc)
tc = hrp.TestCasePath("../examples/demo-with-py-plugin/")
testCases, err = hrp.LoadTestCases(&tc)
if !assert.Nil(t, err) {
t.Fatal()
}
@@ -28,8 +30,8 @@ func TestLoadTestCases(t *testing.T) {
}
// load test cases from single file path
tc = TestCasePath(demoTestCaseWithPluginJSONPath)
testCases, err = LoadTestCases(&tc)
tc = hrp.TestCasePath(demoTestCaseWithPluginJSONPath)
testCases, err = hrp.LoadTestCases(&tc)
if !assert.Nil(t, err) {
t.Fatal()
}
@@ -38,10 +40,10 @@ func TestLoadTestCases(t *testing.T) {
}
// load test cases from TestCase instance
testcase := &TestCase{
Config: NewConfig("TestCase").SetWeight(3),
testcase := &hrp.TestCase{
Config: hrp.NewConfig("TestCase").SetWeight(3),
}
testCases, err = LoadTestCases(testcase)
testCases, err = hrp.LoadTestCases(testcase)
if !assert.Nil(t, err) {
t.Fatal()
}
@@ -50,7 +52,7 @@ func TestLoadTestCases(t *testing.T) {
}
// load test cases from TestCaseJSON
testcaseJSON := TestCaseJSON(`
testcaseJSON := hrp.TestCaseJSON(`
{
"config":{"name":"TestCaseJSON"},
"teststeps":[
@@ -58,7 +60,7 @@ func TestLoadTestCases(t *testing.T) {
{"name": "step2", "shell":{"string": "ls -l"}}
]
}`)
testCases, err = LoadTestCases(&testcaseJSON)
testCases, err = hrp.LoadTestCases(&testcaseJSON)
if !assert.Nil(t, err) {
t.Fatal()
}
@@ -68,13 +70,13 @@ func TestLoadTestCases(t *testing.T) {
}
func TestLoadCase(t *testing.T) {
tcJSON := &TestCaseDef{}
tcYAML := &TestCaseDef{}
err := LoadFileObject(demoTestCaseWithPluginJSONPath, tcJSON)
tcJSON := &hrp.TestCaseDef{}
tcYAML := &hrp.TestCaseDef{}
err := hrp.LoadFileObject(demoTestCaseWithPluginJSONPath, tcJSON)
if !assert.NoError(t, err) {
t.Fatal()
}
err = LoadFileObject(demoTestCaseWithPluginYAMLPath, tcYAML)
err = hrp.LoadFileObject(demoTestCaseWithPluginYAMLPath, tcYAML)
if !assert.NoError(t, err) {
t.Fatal()
}

View File

@@ -1,22 +1,24 @@
package hrp
package tests
import (
"fmt"
"testing"
"github.com/stretchr/testify/assert"
hrp "github.com/httprunner/httprunner/v5"
)
func TestLoadParameters(t *testing.T) {
testData := []struct {
configParameters map[string]interface{}
loadedParameters map[string]Parameters
loadedParameters map[string]hrp.Parameters
}{
{
map[string]interface{}{
"username-password": fmt.Sprintf("${parameterize(%s/$file)}", hrpExamplesDir),
},
map[string]Parameters{
map[string]hrp.Parameters{
"username-password": {
{"username": "test1", "password": "111111"},
{"username": "test2", "password": "222222"},
@@ -33,7 +35,7 @@ func TestLoadParameters(t *testing.T) {
"user_agent": []interface{}{"iOS/10.1", "iOS/10.2"},
"app_version": []interface{}{4.0},
},
map[string]Parameters{
map[string]hrp.Parameters{
"username-password": {
{"username": "test1", "password": "111111"},
{"username": "test2", "password": "222222"},
@@ -54,7 +56,7 @@ func TestLoadParameters(t *testing.T) {
[]interface{}{"test2", "222222"},
},
},
map[string]Parameters{
map[string]hrp.Parameters{
"username-password": {
{"username": "test1", "password": "111111"},
{"username": "test2", "password": "222222"},
@@ -74,9 +76,9 @@ func TestLoadParameters(t *testing.T) {
variablesMapping := map[string]interface{}{
"file": "account.csv",
}
parser := newParser()
parser := hrp.NewParser()
for _, data := range testData {
value, err := parser.loadParameters(data.configParameters, variablesMapping)
value, err := parser.LoadParameters(data.configParameters, variablesMapping)
if !assert.Nil(t, err) {
t.Fatal()
}
@@ -109,9 +111,9 @@ func TestLoadParametersError(t *testing.T) {
},
},
}
parser := newParser()
parser := hrp.NewParser()
for _, data := range testData {
_, err := parser.loadParameters(data.configParameters, map[string]interface{}{})
_, err := parser.LoadParameters(data.configParameters, map[string]interface{}{})
if !assert.Error(t, err) {
t.Fatal()
}
@@ -125,28 +127,28 @@ func TestInitParametersIteratorCount(t *testing.T) {
"app_version": []interface{}{4.0}, // 1
}
testData := []struct {
cfg *TConfig
cfg *hrp.TConfig
expectLimit int
}{
// default, no parameters setting
{
&TConfig{
&hrp.TConfig{
Parameters: configParameters,
ParametersSetting: &TParamsConfig{},
ParametersSetting: &hrp.TParamsConfig{},
},
6, // 3 * 2 * 1
},
{
&TConfig{
&hrp.TConfig{
Parameters: configParameters,
},
6, // 3 * 2 * 1
},
// default equals to set overall parameters pick-order to "sequential"
{
&TConfig{
&hrp.TConfig{
Parameters: configParameters,
ParametersSetting: &TParamsConfig{
ParametersSetting: &hrp.TParamsConfig{
PickOrder: "sequential",
},
},
@@ -154,10 +156,10 @@ func TestInitParametersIteratorCount(t *testing.T) {
},
// default equals to set each individual parameters pick-order to "sequential"
{
&TConfig{
&hrp.TConfig{
Parameters: configParameters,
ParametersSetting: &TParamsConfig{
Strategies: map[string]iteratorStrategy{
ParametersSetting: &hrp.TParamsConfig{
Strategies: map[string]hrp.IteratorStrategy{
"username-password": {Name: "user-info", PickOrder: "sequential"},
"user_agent": {Name: "user-identity", PickOrder: "sequential"},
"app_version": {Name: "app-version", PickOrder: "sequential"},
@@ -167,10 +169,10 @@ func TestInitParametersIteratorCount(t *testing.T) {
6, // 3 * 2 * 1
},
{
&TConfig{
&hrp.TConfig{
Parameters: configParameters,
ParametersSetting: &TParamsConfig{
Strategies: map[string]iteratorStrategy{
ParametersSetting: &hrp.TParamsConfig{
Strategies: map[string]hrp.IteratorStrategy{
"user_agent": {Name: "user-identity", PickOrder: "sequential"},
"app_version": {Name: "app-version", PickOrder: "sequential"},
},
@@ -182,9 +184,9 @@ func TestInitParametersIteratorCount(t *testing.T) {
// set overall parameters overall pick-order to "random"
// each random parameters only select one item
{
&TConfig{
&hrp.TConfig{
Parameters: configParameters,
ParametersSetting: &TParamsConfig{
ParametersSetting: &hrp.TParamsConfig{
PickOrder: "random",
},
},
@@ -193,10 +195,10 @@ func TestInitParametersIteratorCount(t *testing.T) {
// set some individual parameters pick-order to "random"
// this will override overall strategy
{
&TConfig{
&hrp.TConfig{
Parameters: configParameters,
ParametersSetting: &TParamsConfig{
Strategies: map[string]iteratorStrategy{
ParametersSetting: &hrp.TParamsConfig{
Strategies: map[string]hrp.IteratorStrategy{
"user_agent": {Name: "user-identity", PickOrder: "random"},
},
},
@@ -204,10 +206,10 @@ func TestInitParametersIteratorCount(t *testing.T) {
3, // 3 * 1 * 1
},
{
&TConfig{
&hrp.TConfig{
Parameters: configParameters,
ParametersSetting: &TParamsConfig{
Strategies: map[string]iteratorStrategy{
ParametersSetting: &hrp.TParamsConfig{
Strategies: map[string]hrp.IteratorStrategy{
"username-password": {Name: "user-info", PickOrder: "random"},
},
},
@@ -217,18 +219,18 @@ func TestInitParametersIteratorCount(t *testing.T) {
// set limit for parameters
{
&TConfig{
&hrp.TConfig{
Parameters: configParameters, // total: 6 = 3 * 2 * 1
ParametersSetting: &TParamsConfig{
ParametersSetting: &hrp.TParamsConfig{
Limit: 4, // limit could be less than total
},
},
4,
},
{
&TConfig{
&hrp.TConfig{
Parameters: configParameters, // total: 6 = 3 * 2 * 1
ParametersSetting: &TParamsConfig{
ParametersSetting: &hrp.TParamsConfig{
Limit: 9, // limit could also be greater than total
},
},
@@ -238,20 +240,20 @@ func TestInitParametersIteratorCount(t *testing.T) {
// no parameters
// also will generate one empty item
{
&TConfig{
&hrp.TConfig{
Parameters: nil,
ParametersSetting: nil,
},
1,
},
}
parser := newParser()
parser := hrp.NewParser()
for _, data := range testData {
iterator, err := parser.initParametersIterator(data.cfg)
iterator, err := parser.InitParametersIterator(data.cfg)
if !assert.Nil(t, err) {
t.Fatal()
}
if !assert.Equal(t, data.expectLimit, iterator.limit) {
if !assert.Equal(t, data.expectLimit, iterator.Limit) {
t.Fatal()
}
@@ -275,34 +277,34 @@ func TestInitParametersIteratorUnlimitedCount(t *testing.T) {
"app_version": []interface{}{4.0}, // 1
}
testData := []struct {
cfg *TConfig
cfg *hrp.TConfig
}{
// default, no parameters setting
{
&TConfig{
&hrp.TConfig{
Parameters: configParameters,
ParametersSetting: &TParamsConfig{},
ParametersSetting: &hrp.TParamsConfig{},
},
},
// no parameters
// also will generate one empty item
{
&TConfig{
&hrp.TConfig{
Parameters: nil,
ParametersSetting: nil,
},
},
}
parser := newParser()
parser := hrp.NewParser()
for _, data := range testData {
iterator, err := parser.initParametersIterator(data.cfg)
iterator, err := parser.InitParametersIterator(data.cfg)
if !assert.Nil(t, err) {
t.Fatal()
}
// set unlimited mode
iterator.SetUnlimitedMode()
if !assert.Equal(t, -1, iterator.limit) {
if !assert.Equal(t, -1, iterator.Limit) {
t.Fatal()
}
@@ -312,7 +314,7 @@ func TestInitParametersIteratorUnlimitedCount(t *testing.T) {
}
iterator.Next() // consume next parameters
}
if !assert.Equal(t, 100, iterator.index) {
if !assert.Equal(t, 100, iterator.Index) {
t.Fatal()
}
// should also have next
@@ -329,13 +331,13 @@ func TestInitParametersIteratorContent(t *testing.T) {
"app_version": []interface{}{4.0}, // 1
}
testData := []struct {
cfg *TConfig
cfg *hrp.TConfig
checkIndex int
expectParameters map[string]interface{}
}{
// default, no parameters setting
{
&TConfig{
&hrp.TConfig{
Parameters: configParameters,
},
0, // check first item
@@ -346,16 +348,16 @@ func TestInitParametersIteratorContent(t *testing.T) {
// set limit for parameters
{
&TConfig{
&hrp.TConfig{
Parameters: map[string]interface{}{
"username-password": []map[string]interface{}{ // 1
{"username": "test1", "password": 111111, "other": "111"},
},
"user_agent": []string{"iOS/10.1", "iOS/10.2"}, // 2
},
ParametersSetting: &TParamsConfig{
ParametersSetting: &hrp.TParamsConfig{
Limit: 5, // limit could also be greater than total
Strategies: map[string]iteratorStrategy{
Strategies: map[string]hrp.IteratorStrategy{
"username-password": {Name: "user-info", PickOrder: "random"},
},
},
@@ -369,7 +371,7 @@ func TestInitParametersIteratorContent(t *testing.T) {
// no parameters
// also will generate one empty item
{
&TConfig{
&hrp.TConfig{
Parameters: nil,
ParametersSetting: nil,
},
@@ -377,9 +379,9 @@ func TestInitParametersIteratorContent(t *testing.T) {
map[string]interface{}{},
},
}
parser := newParser()
parser := hrp.NewParser()
for _, data := range testData {
iterator, err := parser.initParametersIterator(data.cfg)
iterator, err := parser.InitParametersIterator(data.cfg)
if !assert.Nil(t, err) {
t.Fatal()
}
@@ -401,11 +403,11 @@ func TestInitParametersIteratorContent(t *testing.T) {
func TestGenCartesianProduct(t *testing.T) {
testData := []struct {
multiParameters []Parameters
expect Parameters
multiParameters []hrp.Parameters
expect hrp.Parameters
}{
{
[]Parameters{
[]hrp.Parameters{
{
{"app_version": 4.0},
},
@@ -418,7 +420,7 @@ func TestGenCartesianProduct(t *testing.T) {
{"user_agent": "iOS/10.2"},
},
},
Parameters{
hrp.Parameters{
{"app_version": 4.0, "password": "111111", "user_agent": "iOS/10.1", "username": "test1"},
{"app_version": 4.0, "password": "111111", "user_agent": "iOS/10.2", "username": "test1"},
{"app_version": 4.0, "password": "222222", "user_agent": "iOS/10.1", "username": "test2"},
@@ -430,13 +432,13 @@ func TestGenCartesianProduct(t *testing.T) {
nil,
},
{
[]Parameters{},
[]hrp.Parameters{},
nil,
},
}
for _, data := range testData {
parameters := genCartesianProduct(data.multiParameters)
parameters := hrp.GenCartesianProduct(data.multiParameters)
if !assert.Equal(t, data.expect, parameters) {
t.Fatal()
}
@@ -490,7 +492,7 @@ func TestConvertParameters(t *testing.T) {
}
for _, data := range testData {
value, err := convertParameters(data.key, data.parametersRawList)
value, err := hrp.ConvertParameters(data.key, data.parametersRawList)
if !assert.Nil(t, err) {
t.Fatal()
}
@@ -530,7 +532,7 @@ func TestConvertParametersError(t *testing.T) {
}
for _, data := range testData {
_, err := convertParameters(data.key, data.parametersRawList)
_, err := hrp.ConvertParameters(data.key, data.parametersRawList)
if !assert.Error(t, err) {
t.Fatal()
}

View File

@@ -1,43 +1,45 @@
package hrp
package tests
import (
"testing"
"github.com/stretchr/testify/assert"
hrp "github.com/httprunner/httprunner/v5"
)
func TestLocateFile(t *testing.T) {
// specify target file path
_, err := locateFile(tmpl("plugin/debugtalk.go"), PluginGoSourceFile)
_, err := hrp.LocateFile(tmpl("plugin/debugtalk.go"), hrp.PluginGoSourceFile)
if !assert.Nil(t, err) {
t.Fatal()
}
// specify path with the same dir
_, err = locateFile(tmpl("plugin/debugtalk.py"), PluginGoSourceFile)
_, err = hrp.LocateFile(tmpl("plugin/debugtalk.py"), hrp.PluginGoSourceFile)
if !assert.Nil(t, err) {
t.Fatal()
}
// specify target file path dir
_, err = locateFile(tmpl("plugin/"), PluginGoSourceFile)
_, err = hrp.LocateFile(tmpl("plugin/"), hrp.PluginGoSourceFile)
if !assert.Nil(t, err) {
t.Fatal()
}
// specify wrong path
_, err = locateFile(".", PluginGoSourceFile)
_, err = hrp.LocateFile(".", hrp.PluginGoSourceFile)
if !assert.Error(t, err) {
t.Fatal()
}
_, err = locateFile("/abc", PluginGoSourceFile)
_, err = hrp.LocateFile("/abc", hrp.PluginGoSourceFile)
if !assert.Error(t, err) {
t.Fatal()
}
}
func TestLocatePythonPlugin(t *testing.T) {
_, err := locatePlugin(tmpl("plugin/debugtalk.py"))
_, err := hrp.LocatePlugin(tmpl("plugin/debugtalk.py"))
if !assert.Nil(t, err) {
t.Fatal()
}
@@ -47,7 +49,7 @@ func TestLocateGoPlugin(t *testing.T) {
buildHashicorpGoPlugin()
defer removeHashicorpGoPlugin()
_, err := locatePlugin(tmpl("debugtalk.bin"))
_, err := hrp.LocatePlugin(tmpl("debugtalk.bin"))
if !assert.Nil(t, err) {
t.Fatal()
}

View File

@@ -1,22 +1,22 @@
package hrp
package tests
import (
"errors"
"fmt"
"os"
"path/filepath"
"testing"
"time"
"github.com/rs/zerolog/log"
"github.com/stretchr/testify/assert"
hrp "github.com/httprunner/httprunner/v5"
"github.com/httprunner/httprunner/v5/code"
)
func buildHashicorpGoPlugin() {
log.Info().Msg("[init] build hashicorp go plugin")
err := BuildPlugin(tmpl("plugin/debugtalk.go"), tmpl("debugtalk.bin"))
err := hrp.BuildPlugin(tmpl("plugin/debugtalk.go"), tmpl("debugtalk.bin"))
if err != nil {
log.Error().Err(err).Msg("build hashicorp go plugin failed")
os.Exit(code.GetErrorCode(err))
@@ -26,8 +26,6 @@ func buildHashicorpGoPlugin() {
func removeHashicorpGoPlugin() {
log.Info().Msg("[teardown] remove hashicorp go plugin")
os.Remove(tmpl("debugtalk.bin"))
pluginPath, _ := filepath.Abs(tmpl("debugtalk.bin"))
pluginMap.Delete(pluginPath)
}
func buildHashicorpPyPlugin() {
@@ -43,8 +41,8 @@ func buildHashicorpPyPlugin() {
func removeHashicorpPyPlugin() {
log.Info().Msg("[teardown] remove hashicorp python plugin")
// on v4.1^, running case will generate .debugtalk_gen.py used by python plugin
os.Remove(tmpl(PluginPySourceFile))
os.Remove(tmpl(PluginPySourceGenFile))
os.Remove(tmpl(hrp.PluginPySourceFile))
os.Remove(tmpl(hrp.PluginPySourceGenFile))
}
func TestRunCaseWithGoPlugin(t *testing.T) {
@@ -62,21 +60,21 @@ func TestRunCaseWithPythonPlugin(t *testing.T) {
}
func assertRunTestCases(t *testing.T) {
refCase := TestCasePath(demoTestCaseWithPluginJSONPath)
testcase1 := &TestCase{
Config: NewConfig("TestCase1").
refCase := hrp.TestCasePath(demoTestCaseWithPluginJSONPath)
testcase1 := &hrp.TestCase{
Config: hrp.NewConfig("TestCase1").
SetBaseURL("https://postman-echo.com"),
TestSteps: []IStep{
NewStep("testcase1-step1").
TestSteps: []hrp.IStep{
hrp.NewStep("testcase1-step1").
GET("/headers").
Validate().
AssertEqual("status_code", 200, "check status code").
AssertEqual("headers.\"Content-Type\"", "application/json; charset=utf-8", "check http response Content-Type"),
NewStep("testcase1-step2").CallRefCase(
&TestCase{
Config: NewConfig("testcase1-step3-ref-case").SetBaseURL("https://postman-echo.com"),
TestSteps: []IStep{
NewStep("ip").
hrp.NewStep("testcase1-step2").CallRefCase(
&hrp.TestCase{
Config: hrp.NewConfig("testcase1-step3-ref-case").SetBaseURL("https://postman-echo.com"),
TestSteps: []hrp.IStep{
hrp.NewStep("ip").
GET("/ip").
Validate().
AssertEqual("status_code", 200, "check status code").
@@ -84,14 +82,14 @@ func assertRunTestCases(t *testing.T) {
},
},
),
NewStep("testcase1-step3").CallRefCase(&refCase),
hrp.NewStep("testcase1-step3").CallRefCase(&refCase),
},
}
testcase2 := &TestCase{
Config: NewConfig("TestCase2").SetWeight(3),
testcase2 := &hrp.TestCase{
Config: hrp.NewConfig("TestCase2").SetWeight(3),
}
r := NewRunner(t)
r := hrp.NewRunner(t)
r.SetPluginLogOn()
err := r.Run(testcase1, testcase2)
if err != nil {
@@ -103,46 +101,46 @@ func TestRunCaseWithThinkTime(t *testing.T) {
buildHashicorpGoPlugin()
defer removeHashicorpGoPlugin()
testcases := []*TestCase{
testcases := []*hrp.TestCase{
{
Config: NewConfig("TestCase1"),
TestSteps: []IStep{
NewStep("thinkTime").SetThinkTime(2),
Config: hrp.NewConfig("TestCase1"),
TestSteps: []hrp.IStep{
hrp.NewStep("thinkTime").SetThinkTime(2),
},
},
{
Config: NewConfig("TestCase2").
SetThinkTime(thinkTimeIgnore, nil, 0),
TestSteps: []IStep{
NewStep("thinkTime").SetThinkTime(0.5),
Config: hrp.NewConfig("TestCase2").
SetThinkTime(hrp.ThinkTimeIgnore, nil, 0),
TestSteps: []hrp.IStep{
hrp.NewStep("thinkTime").SetThinkTime(0.5),
},
},
{
Config: NewConfig("TestCase3").
SetThinkTime(thinkTimeRandomPercentage, nil, 0),
TestSteps: []IStep{
NewStep("thinkTime").SetThinkTime(1),
Config: hrp.NewConfig("TestCase3").
SetThinkTime(hrp.ThinkTimeRandomPercentage, nil, 0),
TestSteps: []hrp.IStep{
hrp.NewStep("thinkTime").SetThinkTime(1),
},
},
{
Config: NewConfig("TestCase4").
SetThinkTime(thinkTimeRandomPercentage, map[string]interface{}{"min_percentage": 2, "max_percentage": 3}, 2.5),
TestSteps: []IStep{
NewStep("thinkTime").SetThinkTime(1),
Config: hrp.NewConfig("TestCase4").
SetThinkTime(hrp.ThinkTimeRandomPercentage, map[string]interface{}{"min_percentage": 2, "max_percentage": 3}, 2.5),
TestSteps: []hrp.IStep{
hrp.NewStep("thinkTime").SetThinkTime(1),
},
},
{
Config: NewConfig("TestCase5"),
TestSteps: []IStep{
Config: hrp.NewConfig("TestCase5"),
TestSteps: []hrp.IStep{
// think time: 3s, random pct: {"min_percentage":1, "max_percentage":1.5}, limit: 4s
NewStep("thinkTime").CallRefCase(&demoTestCaseWithThinkTimePath),
hrp.NewStep("thinkTime").CallRefCase(&demoTestCaseWithThinkTimePath),
},
},
}
expectedMinValue := []float64{2, 0, 0.5, 2, 3}
expectedMaxValue := []float64{2.5, 0.5, 2, 3, 10}
for idx, testcase := range testcases {
r := NewRunner(t)
r := hrp.NewRunner(t)
startTime := time.Now()
err := r.Run(testcase)
if err != nil {
@@ -158,20 +156,20 @@ func TestRunCaseWithThinkTime(t *testing.T) {
}
func TestRunCaseWithShell(t *testing.T) {
testcase1 := &TestCase{
Config: NewConfig("complex shell with env variables").
testcase1 := &hrp.TestCase{
Config: hrp.NewConfig("complex shell with env variables").
WithVariables(map[string]interface{}{
"SS": "12345",
"ABC": "$SS",
}),
TestSteps: []IStep{
NewStep("shell21").Shell("echo hello world"),
TestSteps: []hrp.IStep{
hrp.NewStep("shell21").Shell("echo hello world"),
// NewStep("shell21").Shell("echo $ABC"),
// NewStep("shell21").Shell("which hrp"),
},
}
r := NewRunner(t)
r := hrp.NewRunner(t)
err := r.Run(testcase1)
if err != nil {
t.Fatal()
@@ -193,16 +191,16 @@ func TestRunCaseWithFunction(t *testing.T) {
err3 = errors.New("func3 error")
fmt.Println("call function3 with return value and error")
}
testcase1 := &TestCase{
Config: NewConfig("call function"),
TestSteps: []IStep{
NewStep("fn1").Function(fn1),
NewStep("fn2").Function(fn2),
NewStep("fn3").Function(fn3),
testcase1 := &hrp.TestCase{
Config: hrp.NewConfig("call function"),
TestSteps: []hrp.IStep{
hrp.NewStep("fn1").Function(fn1),
hrp.NewStep("fn2").Function(fn2),
hrp.NewStep("fn3").Function(fn3),
},
}
r := NewRunner(t)
r := hrp.NewRunner(t)
err := r.Run(testcase1)
if err != nil {
t.Fatal()
@@ -220,8 +218,8 @@ func TestRunCaseWithPluginJSON(t *testing.T) {
buildHashicorpGoPlugin()
defer removeHashicorpGoPlugin()
testCase := TestCasePath(demoTestCaseWithPluginJSONPath)
err := NewRunner(nil).Run(&testCase) // hrp.Run(testCase)
testCase := hrp.TestCasePath(demoTestCaseWithPluginJSONPath)
err := hrp.NewRunner(nil).Run(&testCase) // hrp.Run(testCase)
if err != nil {
t.Fatal()
}
@@ -243,22 +241,22 @@ func TestRunCaseWithRefAPI(t *testing.T) {
buildHashicorpGoPlugin()
defer removeHashicorpGoPlugin()
testCase := TestCasePath(demoTestCaseWithRefAPIPath)
err := NewRunner(nil).Run(&testCase)
testCase := hrp.TestCasePath(demoTestCaseWithRefAPIPath)
err := hrp.NewRunner(nil).Run(&testCase)
if err != nil {
t.Fatal()
}
refAPI := APIPath(demoAPIGETPath)
testcase := &TestCase{
Config: NewConfig("TestCase").
refAPI := hrp.APIPath(demoAPIGETPath)
testcase := &hrp.TestCase{
Config: hrp.NewConfig("TestCase").
SetBaseURL("https://postman-echo.com"),
TestSteps: []IStep{
NewStep("run referenced api").CallRefAPI(&refAPI),
TestSteps: []hrp.IStep{
hrp.NewStep("run referenced api").CallRefAPI(&refAPI),
},
}
r := NewRunner(t)
r := hrp.NewRunner(t)
err = r.Run(testcase)
if err != nil {
t.Fatal()
@@ -266,15 +264,15 @@ func TestRunCaseWithRefAPI(t *testing.T) {
}
func TestSessionRunner(t *testing.T) {
testcase := TestCase{
Config: NewConfig("TestCase").
testcase := hrp.TestCase{
Config: hrp.NewConfig("TestCase").
WithVariables(map[string]interface{}{
"a": 12.3,
"b": 3.45,
"varFoo": "${max($a, $b)}",
}),
TestSteps: []IStep{
NewStep("check variables").
TestSteps: []hrp.IStep{
hrp.NewStep("check variables").
WithVariables(map[string]interface{}{
"a": 12.3,
"b": 34.5,
@@ -287,7 +285,7 @@ func TestSessionRunner(t *testing.T) {
},
}
caseRunner, _ := NewRunner(t).NewCaseRunner(testcase)
caseRunner, _ := hrp.NewRunner(t).NewCaseRunner(testcase)
sessionRunner := caseRunner.NewSession()
step := testcase.TestSteps[0]
if !assert.Equal(t, step.Config().Variables["varFoo"], "${max($a, $b)}") {

View File

@@ -1,40 +1,42 @@
package hrp
package tests
import (
"math"
"testing"
hrp "github.com/httprunner/httprunner/v5"
)
func TestRunCaseWithRendezvous(t *testing.T) {
rendezvousBoundaryTestcase := &TestCase{
Config: NewConfig("run request with functions").
rendezvousBoundaryTestcase := &hrp.TestCase{
Config: hrp.NewConfig("run request with functions").
SetBaseURL("https://postman-echo.com").
WithVariables(map[string]interface{}{
"n": 5,
"a": 12.3,
"b": 3.45,
}),
TestSteps: []IStep{
NewStep("test negative number").
TestSteps: []hrp.IStep{
hrp.NewStep("test negative number").
SetRendezvous("test negative number").
WithUserNumber(-1),
NewStep("test overflow number").
hrp.NewStep("test overflow number").
SetRendezvous("test overflow number").
WithUserNumber(1000000),
NewStep("test negative percent").
hrp.NewStep("test negative percent").
SetRendezvous("test very low percent").
WithUserPercent(-0.5),
NewStep("test very low percent").
hrp.NewStep("test very low percent").
SetRendezvous("test very low percent").
WithUserPercent(0.00001),
NewStep("test overflow percent").
hrp.NewStep("test overflow percent").
SetRendezvous("test overflow percent").
WithUserPercent(1.5),
NewStep("test conflict params").
hrp.NewStep("test conflict params").
SetRendezvous("test conflict params").
WithUserNumber(1).
WithUserPercent(0.123),
NewStep("test negative timeout").
hrp.NewStep("test negative timeout").
SetRendezvous("test negative timeout").
WithTimeout(-1000),
},
@@ -55,7 +57,7 @@ func TestRunCaseWithRendezvous(t *testing.T) {
{number: 100, percent: 1, timeout: 5000},
}
rendezvousList := InitRendezvous(rendezvousBoundaryTestcase, 100)
rendezvousList := hrp.InitRendezvous(rendezvousBoundaryTestcase, 100)
for i, r := range rendezvousList {
if r.Number != expectedRendezvousParams[i].number {

View File

@@ -1,17 +1,15 @@
package hrp
package tests
import (
"io"
"net/http"
"strings"
"testing"
"time"
hrp "github.com/httprunner/httprunner/v5"
"github.com/stretchr/testify/assert"
)
var (
stepGET = NewStep("get with params").
stepGET = hrp.NewStep("get with params").
GET("/get").
WithParams(map[string]interface{}{"foo1": "bar1", "foo2": "bar2"}).
WithHeaders(map[string]string{"User-Agent": "HttpRunnerPlus"}).
@@ -21,7 +19,7 @@ var (
AssertEqual("headers.\"Content-Type\"", "application/json; charset=utf-8", "check header Content-Type").
AssertEqual("body.args.foo1", "bar1", "check param foo1").
AssertEqual("body.args.foo2", "bar2", "check param foo2")
stepPOSTData = NewStep("post form data").
stepPOSTData = hrp.NewStep("post form data").
POST("/post").
WithParams(map[string]interface{}{"foo1": "bar1", "foo2": "bar2"}).
WithHeaders(map[string]string{"User-Agent": "HttpRunnerPlus", "Content-Type": "application/x-www-form-urlencoded"}).
@@ -33,7 +31,7 @@ var (
func TestRunRequestGetToStruct(t *testing.T) {
tStep := stepGET
if tStep.Request.Method != httpGET {
if tStep.Request.Method != hrp.HTTP_GET {
t.Fatalf("tStep.Request.Method != GET")
}
if tStep.Request.URL != "/get" {
@@ -48,7 +46,7 @@ func TestRunRequestGetToStruct(t *testing.T) {
if tStep.Request.Cookies["user"] != "debugtalk" {
t.Fatalf("tStep.Request.Cookies mismatch")
}
validator, ok := tStep.Validators[0].(Validator)
validator, ok := tStep.Validators[0].(hrp.Validator)
if !ok || validator.Check != "status_code" || validator.Expect != 200 {
t.Fatalf("tStep.Validators mismatch")
}
@@ -56,7 +54,7 @@ func TestRunRequestGetToStruct(t *testing.T) {
func TestRunRequestPostDataToStruct(t *testing.T) {
tStep := stepPOSTData
if tStep.Request.Method != httpPOST {
if tStep.Request.Method != hrp.HTTP_POST {
t.Fatalf("tStep.Request.Method != POST")
}
if tStep.Request.URL != "/post" {
@@ -74,18 +72,18 @@ func TestRunRequestPostDataToStruct(t *testing.T) {
if tStep.Request.Body != "a=1&b=2" {
t.Fatalf("tStep.Request.Data mismatch")
}
validator, ok := tStep.Validators[0].(Validator)
validator, ok := tStep.Validators[0].(hrp.Validator)
if !ok || validator.Check != "status_code" || validator.Expect != 200 {
t.Fatalf("tStep.Validators mismatch")
}
}
func TestRunRequestStatOn(t *testing.T) {
testcase := TestCase{
Config: NewConfig("test").SetBaseURL("https://postman-echo.com"),
TestSteps: []IStep{stepGET, stepPOSTData},
testcase := hrp.TestCase{
Config: hrp.NewConfig("test").SetBaseURL("https://postman-echo.com"),
TestSteps: []hrp.IStep{stepGET, stepPOSTData},
}
caseRunner, _ := NewRunner(t).SetHTTPStatOn().NewCaseRunner(testcase)
caseRunner, _ := hrp.NewRunner(t).SetHTTPStatOn().NewCaseRunner(testcase)
sessionRunner := caseRunner.NewSession()
summary, err := sessionRunner.Start(nil)
if err != nil {
@@ -162,15 +160,15 @@ func TestRunRequestStatOn(t *testing.T) {
}
func TestRunCaseWithTimeout(t *testing.T) {
r := NewRunner(t)
r := hrp.NewRunner(t)
// global timeout
testcase1 := &TestCase{
Config: NewConfig("TestCase1").
testcase1 := &hrp.TestCase{
Config: hrp.NewConfig("TestCase1").
SetRequestTimeout(10). // set global timeout to 10s
SetBaseURL("https://postman-echo.com"),
TestSteps: []IStep{
NewStep("step1").
TestSteps: []hrp.IStep{
hrp.NewStep("step1").
GET("/delay/1").
Validate().
AssertEqual("status_code", 200, "check status code"),
@@ -181,12 +179,12 @@ func TestRunCaseWithTimeout(t *testing.T) {
t.FailNow()
}
testcase2 := &TestCase{
Config: NewConfig("TestCase2").
testcase2 := &hrp.TestCase{
Config: hrp.NewConfig("TestCase2").
SetRequestTimeout(5). // set global timeout to 10s
SetBaseURL("https://postman-echo.com"),
TestSteps: []IStep{
NewStep("step1").
TestSteps: []hrp.IStep{
hrp.NewStep("step1").
GET("/delay/10").
Validate().
AssertEqual("status_code", 200, "check status code"),
@@ -198,12 +196,12 @@ func TestRunCaseWithTimeout(t *testing.T) {
}
// step timeout
testcase3 := &TestCase{
Config: NewConfig("TestCase3").
testcase3 := &hrp.TestCase{
Config: hrp.NewConfig("TestCase3").
SetRequestTimeout(10).
SetBaseURL("https://postman-echo.com"),
TestSteps: []IStep{
NewStep("step2").
TestSteps: []hrp.IStep{
hrp.NewStep("step2").
GET("/delay/11").
SetTimeout(15*time.Second). // set step timeout to 4s
Validate().
@@ -215,70 +213,3 @@ func TestRunCaseWithTimeout(t *testing.T) {
t.FailNow()
}
}
func TestSearchJmespath(t *testing.T) {
testText := `{"a": {"b": "foo"}, "c": "bar", "d": {"e": [{"f": "foo"}, {"f": "bar"}]}}`
testData := []struct {
raw string
expected string
}{
{"body.a.b", "foo"},
{"body.c", "bar"},
{"body.d.e[0].f", "foo"},
{"body.d.e[1].f", "bar"},
}
resp := http.Response{}
resp.Body = io.NopCloser(strings.NewReader(testText))
respObj, err := newHttpResponseObject(t, newParser(), &resp)
if err != nil {
t.Fatal()
}
for _, data := range testData {
if !assert.Equal(t, data.expected, respObj.searchJmespath(data.raw)) {
t.Fatal()
}
}
}
func TestSearchRegexp(t *testing.T) {
testText := `
<ul class="nav navbar-nav navbar-right">
<li><a href="/order/addToCart" style="color: white"><i class="fa fa-shopping-cart fa-2x"></i><span class="badge">0</span></a></li>
<li class="dropdown">
<a class="dropdown-toggle" data-toggle="dropdown" href="#" style="color: white">
Leo <i class="fa fa-cog fa-2x"></i><span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="/user/changePassword">Change Password</a></li>
<li><a href="/user/addAddress">Shipping</a></li>
<li><a href="/user/addCard">Payment</a></li>
<li><a href="/order/orderHistory">Order History</a></li>
<li><a href="/user/signOut">Sign Out</a></li>
</ul>
</li>
<li>&nbsp;&nbsp;&nbsp;</li>
<li><a href="/user/signOut" style="color: white"><i class="fa fa-sign-out fa-2x"></i>
Sign Out</a></li>
</ul>
`
testData := []struct {
raw string
expected string
}{
{"/user/signOut\">(.*)</a></li>", "Sign Out"},
{"<li><a href=\"/user/(.*)\" style", "signOut"},
{" (.*) <i class=\"fa fa-cog fa-2x\"></i>", "Leo"},
}
// new response object
resp := http.Response{}
resp.Body = io.NopCloser(strings.NewReader(testText))
respObj, err := newHttpResponseObject(t, newParser(), &resp)
if err != nil {
t.Fatal()
}
for _, data := range testData {
if !assert.Equal(t, data.expected, respObj.searchRegexp(data.raw)) {
t.Fatal()
}
}
}

View File

@@ -1,87 +1,88 @@
//go:build localtest
package hrp
package tests
import (
"testing"
hrp "github.com/httprunner/httprunner/v5"
"github.com/httprunner/httprunner/v5/uixt/option"
)
func TestIOSSettingsAction(t *testing.T) {
testCase := &TestCase{
Config: NewConfig("ios ui action on Settings").
testCase := &hrp.TestCase{
Config: hrp.NewConfig("ios ui action on Settings").
SetIOS(option.WithWDAPort(8700), option.WithWDAMjpegPort(8800)),
TestSteps: []IStep{
NewStep("launch Settings").
TestSteps: []hrp.IStep{
hrp.NewStep("launch Settings").
IOS().Home().TapByOCR("设置").
Validate().
AssertNameExists("飞行模式").
AssertLabelExists("蓝牙").
AssertOCRExists("个人热点"),
NewStep("swipe up and down").
hrp.NewStep("swipe up and down").
IOS().SwipeUp().SwipeUp().SwipeDown(),
},
}
err := NewRunner(t).Run(testCase)
err := hrp.NewRunner(t).Run(testCase)
if err != nil {
t.Fatal(err)
}
}
func TestIOSSearchApp(t *testing.T) {
testCase := &TestCase{
Config: NewConfig("ios ui action on Search App 资源库"),
TestSteps: []IStep{
NewStep("进入 App 资源库 搜索框").
testCase := &hrp.TestCase{
Config: hrp.NewConfig("ios ui action on Search App 资源库"),
TestSteps: []hrp.IStep{
hrp.NewStep("进入 App 资源库 搜索框").
IOS().Home().SwipeLeft().SwipeLeft().TapByCV("dewey-search-field").
Validate().
AssertLabelExists("取消"),
NewStep("搜索抖音").
hrp.NewStep("搜索抖音").
IOS().Input("抖音\n"),
},
}
err := NewRunner(t).Run(testCase)
err := hrp.NewRunner(t).Run(testCase)
if err != nil {
t.Fatal(err)
}
}
func TestIOSAppLaunch(t *testing.T) {
testCase := &TestCase{
Config: NewConfig("启动 & 关闭 App").
testCase := &hrp.TestCase{
Config: hrp.NewConfig("启动 & 关闭 App").
SetIOS(option.WithWDAPort(8700), option.WithWDAMjpegPort(8800)),
TestSteps: []IStep{
NewStep("终止今日头条").
TestSteps: []hrp.IStep{
hrp.NewStep("终止今日头条").
IOS().AppTerminate("com.ss.iphone.article.News"),
NewStep("启动今日头条").
hrp.NewStep("启动今日头条").
IOS().AppLaunch("com.ss.iphone.article.News"),
NewStep("终止今日头条").
hrp.NewStep("终止今日头条").
IOS().AppTerminate("com.ss.iphone.article.News"),
NewStep("启动今日头条").
hrp.NewStep("启动今日头条").
IOS().AppLaunch("com.ss.iphone.article.News"),
},
}
err := NewRunner(t).Run(testCase)
err := hrp.NewRunner(t).Run(testCase)
if err != nil {
t.Fatal(err)
}
}
func TestAndroidAction(t *testing.T) {
testCase := &TestCase{
Config: NewConfig("android ui action"),
TestSteps: []IStep{
NewStep("launch douyin").
testCase := &hrp.TestCase{
Config: hrp.NewConfig("android ui action"),
TestSteps: []hrp.IStep{
hrp.NewStep("launch douyin").
Android().Serial("xxx").TapByOCR("抖音").
Validate().
AssertNameExists("首页", "首页 tab 不存在").
AssertNameExists("消息", "消息 tab 不存在"),
NewStep("swipe up and down").
hrp.NewStep("swipe up and down").
Android().Serial("xxx").SwipeUp().SwipeUp().SwipeDown(),
},
}
err := NewRunner(t).Run(testCase)
err := hrp.NewRunner(t).Run(testCase)
if err != nil {
t.Fatal(err)
}

View File

@@ -1,23 +1,25 @@
package hrp
package tests
import (
"testing"
"github.com/stretchr/testify/assert"
hrp "github.com/httprunner/httprunner/v5"
)
func TestGenHTMLReport(t *testing.T) {
summary := NewSummary()
summary := hrp.NewSummary()
caseSummary1 := NewCaseSummary()
stepResult1 := &StepResult{}
caseSummary1 := hrp.NewCaseSummary()
stepResult1 := &hrp.StepResult{}
caseSummary1.AddStepResult(stepResult1)
summary.AddCaseSummary(caseSummary1)
caseSummary2 := NewCaseSummary()
stepResult2 := &StepResult{
caseSummary2 := hrp.NewCaseSummary()
stepResult2 := &hrp.StepResult{
Name: "Test",
StepType: StepTypeRequest,
StepType: hrp.StepTypeRequest,
Success: false,
ContentSize: 0,
Attachments: "err",
@@ -37,22 +39,22 @@ func TestGenHTMLReport(t *testing.T) {
}
func TestTestCaseSummary_AddStepResult(t *testing.T) {
caseSummary := NewCaseSummary()
stepResult1 := &StepResult{
caseSummary := hrp.NewCaseSummary()
stepResult1 := &hrp.StepResult{
Name: "Test1",
StepType: StepTypeRequest,
StepType: hrp.StepTypeRequest,
Success: true,
ContentSize: 0,
Attachments: "err",
}
caseSummary.AddStepResult(stepResult1)
stepResult2 := &StepResult{
stepResult2 := &hrp.StepResult{
Name: "Test2",
StepType: StepTypeTestCase,
StepType: hrp.StepTypeTestCase,
Success: false,
ContentSize: 0,
Attachments: "err",
Data: []*StepResult{stepResult1},
Data: []*hrp.StepResult{stepResult1},
}
caseSummary.AddStepResult(stepResult2)

View File

@@ -1,10 +1,10 @@
package hrp
package tests
import (
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
hrp "github.com/httprunner/httprunner/v5"
)
const (
@@ -25,10 +25,10 @@ var (
demoAPIGETPath = tmpl("/api/get.yml")
)
var demoTestCaseWithThinkTimePath TestCasePath = hrpExamplesDir + "/think_time_test.json"
var demoTestCaseWithThinkTimePath hrp.TestCasePath = hrpExamplesDir + "/think_time_test.json"
var demoTestCaseWithPlugin = &TestCase{
Config: NewConfig("demo with complex mechanisms").
var demoTestCaseWithPlugin = &hrp.TestCase{
Config: hrp.NewConfig("demo with complex mechanisms").
SetBaseURL("https://postman-echo.com").
WithVariables(map[string]interface{}{ // global level variables
"n": "${sum_ints(1, 2, 2)}",
@@ -37,9 +37,9 @@ var demoTestCaseWithPlugin = &TestCase{
"varFoo1": "${gen_random_string($n)}",
"varFoo2": "${max($a, $b)}", // 12.3; eval with built-in function
}),
TestSteps: []IStep{
NewStep("transaction 1 start").StartTransaction("tran1"), // start transaction
NewStep("get with params").
TestSteps: []hrp.IStep{
hrp.NewStep("transaction 1 start").StartTransaction("tran1"), // start transaction
hrp.NewStep("get with params").
WithVariables(map[string]interface{}{ // step level variables
"n": 3, // inherit config level variables if not set in step level, a/varFoo1
"b": 34.5, // override config level variable if existed, n/b/varFoo2
@@ -59,8 +59,8 @@ var demoTestCaseWithPlugin = &TestCase{
AssertLengthEqual("body.args.foo1", 5, "check args foo1"). // validate response body with jmespath
AssertLengthEqual("$varFoo1", 5, "check args foo1"). // assert with extracted variable from current step
AssertEqual("body.args.foo2", "34.5", "check args foo2"), // notice: request params value will be converted to string
NewStep("transaction 1 end").EndTransaction("tran1"), // end transaction
NewStep("post json data").
hrp.NewStep("transaction 1 end").EndTransaction("tran1"), // end transaction
hrp.NewStep("post json data").
POST("/post").
WithBody(map[string]interface{}{
"foo1": "$varFoo1", // reference former extracted variable
@@ -70,7 +70,7 @@ var demoTestCaseWithPlugin = &TestCase{
AssertEqual("status_code", 200, "check status code").
AssertLengthEqual("body.json.foo1", 5, "check args foo1").
AssertEqual("body.json.foo2", 12.3, "check args foo2"),
NewStep("post form data").
hrp.NewStep("post form data").
POST("/post").
WithHeaders(map[string]string{"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"}).
WithBody(map[string]interface{}{
@@ -84,15 +84,15 @@ var demoTestCaseWithPlugin = &TestCase{
AssertEqual("status_code", 200, "check status code").
AssertLengthEqual("body.form.foo1", 5, "check args foo1").
AssertEqual("body.form.foo2", "12.3", "check args foo2"), // form data will be converted to string
NewStep("get with timestamp").
hrp.NewStep("get with timestamp").
GET("/get").WithParams(map[string]interface{}{"time": "$varTime"}).
Validate().
AssertLengthEqual("body.args.time", 13, "check extracted var timestamp"),
},
}
var demoTestCaseWithoutPlugin = &TestCase{
Config: NewConfig("demo without custom function plugin").
var demoTestCaseWithoutPlugin = &hrp.TestCase{
Config: hrp.NewConfig("demo without custom function plugin").
SetBaseURL("https://postman-echo.com").
WithVariables(map[string]interface{}{ // global level variables
"n": 5,
@@ -101,9 +101,9 @@ var demoTestCaseWithoutPlugin = &TestCase{
"varFoo1": "${gen_random_string($n)}",
"varFoo2": "${max($a, $b)}", // 12.3; eval with built-in function
}),
TestSteps: []IStep{
NewStep("transaction 1 start").StartTransaction("tran1"), // start transaction
NewStep("get with params").
TestSteps: []hrp.IStep{
hrp.NewStep("transaction 1 start").StartTransaction("tran1"), // start transaction
hrp.NewStep("get with params").
WithVariables(map[string]interface{}{ // step level variables
"n": 3, // inherit config level variables if not set in step level, a/varFoo1
"b": 34.5, // override config level variable if existed, n/b/varFoo2
@@ -121,8 +121,8 @@ var demoTestCaseWithoutPlugin = &TestCase{
AssertLengthEqual("body.args.foo1", 5, "check args foo1"). // validate response body with jmespath
AssertLengthEqual("$varFoo1", 5, "check args foo1"). // assert with extracted variable from current step
AssertEqual("body.args.foo2", "34.5", "check args foo2"), // notice: request params value will be converted to string
NewStep("transaction 1 end").EndTransaction("tran1"), // end transaction
NewStep("post json data").
hrp.NewStep("transaction 1 end").EndTransaction("tran1"), // end transaction
hrp.NewStep("post json data").
POST("/post").
WithBody(map[string]interface{}{
"foo1": "$varFoo1", // reference former extracted variable
@@ -132,7 +132,7 @@ var demoTestCaseWithoutPlugin = &TestCase{
AssertEqual("status_code", 200, "check status code").
AssertLengthEqual("body.json.foo1", 5, "check args foo1").
AssertEqual("body.json.foo2", 12.3, "check args foo2"),
NewStep("post form data").
hrp.NewStep("post form data").
POST("/post").
WithHeaders(map[string]string{"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"}).
WithBody(map[string]interface{}{
@@ -146,7 +146,7 @@ var demoTestCaseWithoutPlugin = &TestCase{
AssertEqual("status_code", 200, "check status code").
AssertLengthEqual("body.form.foo1", 5, "check args foo1").
AssertEqual("body.form.foo2", "12.3", "check args foo2"), // form data will be converted to string
NewStep("get with timestamp").
hrp.NewStep("get with timestamp").
GET("/get").WithParams(map[string]interface{}{"time": "$varTime"}).
Validate().
AssertLengthEqual("body.args.time", 13, "check extracted var timestamp"),
@@ -172,29 +172,3 @@ func TestGenDemoTestCase(t *testing.T) {
t.Fatal()
}
}
func TestConvertCheckExpr(t *testing.T) {
exprs := []struct {
before string
after string
}{
// normal check expression
{"a.b.c", "a.b.c"},
{"a.\"b-c\".d", "a.\"b-c\".d"},
{"a.b-c.d", "a.b-c.d"},
{"body.args.a[-1]", "body.args.a[-1]"},
// check expression using regex
{"covering (.*) testing,", "covering (.*) testing,"},
{" (.*) a-b-c", " (.*) a-b-c"},
// abnormal check expression
{"headers.Content-Type", "headers.\"Content-Type\""},
{"headers.\"Content-Type", "headers.\"Content-Type\""},
{"headers.Content-Type\"", "headers.\"Content-Type\""},
{"headers.User-Agent", "headers.\"User-Agent\""},
}
for _, expr := range exprs {
if !assert.Equal(t, expr.after, convertJmespathExpr(expr.before)) {
t.Fatal()
}
}
}