refactor: move tests

This commit is contained in:
lilong.129
2025-03-05 11:47:08 +08:00
parent e107389d6e
commit b9db874f38
25 changed files with 617 additions and 604 deletions
+2 -2
View File
@@ -10,11 +10,11 @@ import (
"regexp" "regexp"
"strings" "strings"
"github.com/httprunner/funplugin/fungo"
"github.com/httprunner/funplugin/myexec"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/rs/zerolog/log" "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/code"
"github.com/httprunner/httprunner/v5/internal/builtin" "github.com/httprunner/httprunner/v5/internal/builtin"
"github.com/httprunner/httprunner/v5/internal/config" "github.com/httprunner/httprunner/v5/internal/config"
-141
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()
}
}
+12 -12
View File
@@ -80,7 +80,7 @@ func (c *TConfig) WithParameters(parameters map[string]interface{}) *TConfig {
} }
// SetThinkTime sets think time config for current testcase. // 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} c.ThinkTimeSetting = &ThinkTimeConfig{strategy, cfg, limit}
return c return c
} }
@@ -194,7 +194,7 @@ func (c *TConfig) DisableAutoPopupHandler() *TConfig {
} }
type ThinkTimeConfig struct { 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 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 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 // unset strategy, set default strategy
if ttc.Strategy == "" { if ttc.Strategy == "" {
ttc.Strategy = thinkTimeDefault ttc.Strategy = ThinkTimeDefault
} }
// check think time // check think time
if ttc.Strategy == thinkTimeRandomPercentage { if ttc.Strategy == ThinkTimeRandomPercentage {
if ttc.Setting == nil || reflect.TypeOf(ttc.Setting).Kind() != reflect.Map { if ttc.Setting == nil || reflect.TypeOf(ttc.Setting).Kind() != reflect.Map {
ttc.Setting = thinkTimeDefaultRandom ttc.Setting = thinkTimeDefaultRandom
return return
@@ -237,7 +237,7 @@ func (ttc *ThinkTimeConfig) checkThinkTime() {
return return
} }
ttc.Setting = map[string]float64{"min_percentage": left, "max_percentage": right} 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 { if ttc.Setting == nil {
ttc.Setting = float64(0) // default ttc.Setting = float64(0) // default
return return
@@ -248,19 +248,19 @@ func (ttc *ThinkTimeConfig) checkThinkTime() {
return return
} }
ttc.Setting = value ttc.Setting = value
} else if ttc.Strategy != thinkTimeIgnore { } else if ttc.Strategy != ThinkTimeIgnore {
// unrecognized strategy, set default strategy // unrecognized strategy, set default strategy
ttc.Strategy = thinkTimeDefault ttc.Strategy = ThinkTimeDefault
} }
} }
type thinkTimeStrategy string type ThinkTimeStrategy string
const ( const (
thinkTimeDefault thinkTimeStrategy = "default" // as recorded ThinkTimeDefault ThinkTimeStrategy = "default" // as recorded
thinkTimeRandomPercentage thinkTimeStrategy = "random_percentage" // use random percentage of recorded think time ThinkTimeRandomPercentage ThinkTimeStrategy = "random_percentage" // use random percentage of recorded think time
thinkTimeMultiply thinkTimeStrategy = "multiply" // multiply recorded think time ThinkTimeMultiply ThinkTimeStrategy = "multiply" // multiply recorded think time
thinkTimeIgnore thinkTimeStrategy = "ignore" // ignore recorded think time ThinkTimeIgnore ThinkTimeStrategy = "ignore" // ignore recorded think time
) )
const ( const (
+16
View File
@@ -225,6 +225,22 @@ func InterfaceType(raw interface{}) string {
return reflect.TypeOf(raw).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{} { func loadFromCSV(path string) []map[string]interface{} {
log.Info().Str("path", path).Msg("load csv file") log.Info().Str("path", path).Msg("load csv file")
file, err := os.ReadFile(path) file, err := os.ReadFile(path)
+1 -1
View File
@@ -1 +1 @@
v5.0.0+2503051104 v5.0.0+2503051147
+2 -17
View File
@@ -12,6 +12,7 @@ import (
"gopkg.in/yaml.v2" "gopkg.in/yaml.v2"
"github.com/httprunner/httprunner/v5/code" "github.com/httprunner/httprunner/v5/code"
"github.com/httprunner/httprunner/v5/internal/builtin"
"github.com/httprunner/httprunner/v5/internal/json" "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 // LoadFileObject loads file content with file extension and assigns to structObj
func LoadFileObject(path string, structObj interface{}) (err error) { func LoadFileObject(path string, structObj interface{}) (err error) {
log.Info().Str("path", path).Msg("load file") log.Info().Str("path", path).Msg("load file")
file, err := readFile(path) file, err := builtin.LoadFile(path)
if err != nil { if err != nil {
return errors.Wrap(err, "read file failed") return errors.Wrap(err, "read file failed")
} }
@@ -145,19 +146,3 @@ func parseEnvContent(file []byte, obj interface{}) error {
} }
return nil 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
}
+29 -29
View File
@@ -13,7 +13,7 @@ import (
type TParamsConfig struct { type TParamsConfig struct {
PickOrder iteratorPickOrder `json:"pick_order,omitempty" yaml:"pick_order,omitempty"` // overall pick-order strategy 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"` Limit int `json:"limit,omitempty" yaml:"limit,omitempty"`
} }
@@ -35,13 +35,13 @@ const (
*/ */
type Parameters []map[string]interface{} type Parameters []map[string]interface{}
type iteratorStrategy struct { type IteratorStrategy struct {
Name string `json:"name,omitempty" yaml:"name,omitempty"` Name string `json:"name,omitempty" yaml:"name,omitempty"`
PickOrder iteratorPickOrder `json:"pick_order,omitempty" yaml:"pick_order,omitempty"` PickOrder iteratorPickOrder `json:"pick_order,omitempty" yaml:"pick_order,omitempty"`
} }
func (p *Parser) initParametersIterator(cfg *TConfig) (*ParametersIterator, error) { func (p *Parser) InitParametersIterator(cfg *TConfig) (*ParametersIterator, error) {
parameters, err := p.loadParameters(cfg.Parameters, cfg.Variables) parameters, err := p.LoadParameters(cfg.Parameters, cfg.Variables)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -57,13 +57,13 @@ func newParametersIterator(parameters map[string]Parameters, config *TParamsConf
hasNext: true, hasNext: true,
sequentialParameters: nil, sequentialParameters: nil,
randomParameterNames: nil, randomParameterNames: nil,
limit: config.Limit, Limit: config.Limit,
index: 0, Index: 0,
} }
if len(parameters) == 0 { if len(parameters) == 0 {
iterator.data = map[string]Parameters{} iterator.data = map[string]Parameters{}
iterator.limit = 1 iterator.Limit = 1
return iterator return iterator
} }
@@ -85,24 +85,24 @@ func newParametersIterator(parameters map[string]Parameters, config *TParamsConf
} }
// generate cartesian product for sequential parameters // 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") 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 // limit not set
if len(iterator.sequentialParameters) > 0 { if len(iterator.sequentialParameters) > 0 {
// use cartesian product of sequential parameters size as limit // use cartesian product of sequential parameters size as limit
iterator.limit = len(iterator.sequentialParameters) iterator.Limit = len(iterator.sequentialParameters)
} else { } else {
// all parameters are selected by random // all parameters are selected by random
// only run once // only run once
iterator.limit = 1 iterator.Limit = 1
} }
} else { // limit > 0 } 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 return iterator
@@ -114,14 +114,14 @@ type ParametersIterator struct {
hasNext bool // cache query result hasNext bool // cache query result
sequentialParameters Parameters // cartesian product for sequential parameters sequentialParameters Parameters // cartesian product for sequential parameters
randomParameterNames []string // value is parameter names randomParameterNames []string // value is parameter names
limit int // limit count for iteration Limit int // limit count for iteration
index int // current iteration index Index int // current iteration index
} }
// SetUnlimitedMode is used for load testing // SetUnlimitedMode is used for load testing
func (iter *ParametersIterator) SetUnlimitedMode() { func (iter *ParametersIterator) SetUnlimitedMode() {
log.Info().Msg("set parameters unlimited mode") log.Info().Msg("set parameters unlimited mode")
iter.limit = -1 iter.Limit = -1
} }
func (iter *ParametersIterator) HasNext() bool { func (iter *ParametersIterator) HasNext() bool {
@@ -130,12 +130,12 @@ func (iter *ParametersIterator) HasNext() bool {
} }
// unlimited mode // unlimited mode
if iter.limit == -1 { if iter.Limit == -1 {
return true return true
} }
// reached limit // reached limit
if iter.index >= iter.limit { if iter.Index >= iter.Limit {
// cache query result // cache query result
iter.hasNext = false iter.hasNext = false
return false return false
@@ -155,11 +155,11 @@ func (iter *ParametersIterator) Next() map[string]interface{} {
var selectedParameters map[string]interface{} var selectedParameters map[string]interface{}
if len(iter.sequentialParameters) == 0 { if len(iter.sequentialParameters) == 0 {
selectedParameters = make(map[string]interface{}) selectedParameters = make(map[string]interface{})
} else if iter.index < len(iter.sequentialParameters) { } else if iter.Index < len(iter.sequentialParameters) {
selectedParameters = iter.sequentialParameters[iter.index] selectedParameters = iter.sequentialParameters[iter.Index]
} else { } else {
// loop back to the first sequential parameter // loop back to the first sequential parameter
index := iter.index % len(iter.sequentialParameters) index := iter.Index % len(iter.sequentialParameters)
selectedParameters = iter.sequentialParameters[index] selectedParameters = iter.sequentialParameters[index]
} }
@@ -172,8 +172,8 @@ func (iter *ParametersIterator) Next() map[string]interface{} {
} }
} }
iter.index++ iter.Index++
if iter.limit > 0 && iter.index >= iter.limit { if iter.Limit > 0 && iter.Index >= iter.Limit {
iter.hasNext = false iter.hasNext = false
} }
@@ -188,7 +188,7 @@ func (iter *ParametersIterator) Data() map[string]interface{} {
return res return res
} }
func genCartesianProduct(multiParameters []Parameters) Parameters { func GenCartesianProduct(multiParameters []Parameters) Parameters {
if len(multiParameters) == 0 { if len(multiParameters) == 0 {
return nil 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: 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) { map[string]Parameters, error) {
if len(configParameters) == 0 { 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") return nil, errors.New("config parameters raw value format error")
} }
parameterSlice, err := convertParameters(k, parametersRawList) parameterSlice, err := ConvertParameters(k, parametersRawList)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -320,7 +320,7 @@ case 3:
key = "username-password" key = "username-password"
parametersRawList = [["test1", "111111"], ["test2", "222222"]] 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) parametersRawSlice := reflect.ValueOf(parametersRawList)
if parametersRawSlice.Kind() != reflect.Slice { if parametersRawSlice.Kind() != reflect.Slice {
return nil, errors.New("parameters raw value is not list") return nil, errors.New("parameters raw value is not list")
+3 -3
View File
@@ -10,17 +10,17 @@ import (
"strconv" "strconv"
"strings" "strings"
"github.com/httprunner/funplugin"
"github.com/httprunner/funplugin/fungo"
"github.com/maja42/goval" "github.com/maja42/goval"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/rs/zerolog/log" "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/code"
"github.com/httprunner/httprunner/v5/internal/builtin" "github.com/httprunner/httprunner/v5/internal/builtin"
) )
func newParser() *Parser { func NewParser() *Parser {
return &Parser{} return &Parser{}
} }
+202 -9
View File
@@ -1,8 +1,11 @@
package hrp package hrp
import ( import (
"io"
"net/http"
"net/url" "net/url"
"sort" "sort"
"strings"
"testing" "testing"
"time" "time"
@@ -208,7 +211,7 @@ func TestParseDataStringWithVariables(t *testing.T) {
{"abc$var_5", "abctrue"}, // "abcTrue" {"abc$var_5", "abctrue"}, // "abcTrue"
} }
parser := newParser() parser := NewParser()
for _, data := range testData { for _, data := range testData {
parsedData, err := parser.Parse(data.expr, variablesMapping) parsedData, err := parser.Parse(data.expr, variablesMapping)
if !assert.NoError(t, err) { if !assert.NoError(t, err) {
@@ -233,7 +236,7 @@ func TestParseDataStringWithUndefinedVariables(t *testing.T) {
{"/api/$SECRET_KEY", "/api/$SECRET_KEY"}, // raise error {"/api/$SECRET_KEY", "/api/$SECRET_KEY"}, // raise error
} }
parser := newParser() parser := NewParser()
for _, data := range testData { for _, data := range testData {
parsedData, err := parser.Parse(data.expr, variablesMapping) parsedData, err := parser.Parse(data.expr, variablesMapping)
if !assert.Error(t, err) { if !assert.Error(t, err) {
@@ -278,7 +281,7 @@ func TestParseDataStringWithVariablesAbnormal(t *testing.T) {
{"ABC$var_1{}a", "ABCabc{}a"}, // {} {"ABC$var_1{}a", "ABCabc{}a"}, // {}
} }
parser := newParser() parser := NewParser()
for _, data := range testData { for _, data := range testData {
parsedData, err := parser.Parse(data.expr, variablesMapping) parsedData, err := parser.Parse(data.expr, variablesMapping)
if !assert.NoError(t, err) { if !assert.NoError(t, err) {
@@ -309,7 +312,7 @@ func TestParseDataMapWithVariables(t *testing.T) {
{map[string]interface{}{"$var2": "$val1"}, map[string]interface{}{"123": 200}}, {map[string]interface{}{"$var2": "$val1"}, map[string]interface{}{"123": 200}},
} }
parser := newParser() parser := NewParser()
for _, data := range testData { for _, data := range testData {
parsedData, err := parser.Parse(data.expr, variablesMapping) parsedData, err := parser.Parse(data.expr, variablesMapping)
if !assert.NoError(t, err) { if !assert.NoError(t, err) {
@@ -343,7 +346,7 @@ func TestParseHeaders(t *testing.T) {
{map[string]string{"$var2": "$val2"}, map[string]string{"123": "<nil>"}}, {map[string]string{"$var2": "$val2"}, map[string]string{"123": "<nil>"}},
} }
parser := newParser() parser := NewParser()
for _, data := range testData { for _, data := range testData {
parsedHeaders, err := parser.ParseHeaders(data.rawHeaders, variablesMapping) parsedHeaders, err := parser.ParseHeaders(data.rawHeaders, variablesMapping)
if !assert.NoError(t, err) { if !assert.NoError(t, err) {
@@ -488,7 +491,7 @@ func TestMergeValidators(t *testing.T) {
} }
func TestCallBuiltinFunction(t *testing.T) { func TestCallBuiltinFunction(t *testing.T) {
parser := newParser() parser := NewParser()
// call function without arguments // call function without arguments
_, err := parser.callFunc("get_timestamp") _, err := parser.callFunc("get_timestamp")
@@ -601,7 +604,7 @@ func TestParseDataStringWithFunctions(t *testing.T) {
{"123${gen_random_string($n)}abc", 11}, {"123${gen_random_string($n)}abc", 11},
} }
parser := newParser() parser := NewParser()
for _, data := range testData1 { for _, data := range testData1 {
value, err := parser.Parse(data.expr, variablesMapping) value, err := parser.Parse(data.expr, variablesMapping)
if !assert.NoError(t, err) { if !assert.NoError(t, err) {
@@ -670,7 +673,7 @@ func TestParseVariables(t *testing.T) {
}, },
} }
parser := newParser() parser := NewParser()
for _, data := range testData { for _, data := range testData {
value, err := parser.ParseVariables(data.rawVars) value, err := parser.ParseVariables(data.rawVars)
if !assert.NoError(t, err) { if !assert.NoError(t, err) {
@@ -701,7 +704,7 @@ func TestParseVariablesAbnormal(t *testing.T) {
}, },
} }
parser := newParser() parser := NewParser()
for _, data := range testData { for _, data := range testData {
value, err := parser.ParseVariables(data.rawVars) value, err := parser.ParseVariables(data.rawVars)
if !assert.Error(t, err) { 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()
}
}
+12 -12
View File
@@ -6,11 +6,11 @@ import (
"strings" "strings"
"sync" "sync"
"github.com/httprunner/funplugin"
"github.com/httprunner/funplugin/myexec"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/rs/zerolog/log" "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/code"
"github.com/httprunner/httprunner/v5/internal/config" "github.com/httprunner/httprunner/v5/internal/config"
"github.com/httprunner/httprunner/v5/internal/sdk" "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 == "" { if path == "" {
return nil, nil return nil, nil
} }
pluginPath, err := locatePlugin(path) pluginPath, err := LocatePlugin(path)
if err != nil { if err != nil {
log.Warn().Str("path", path).Msg("locate plugin failed") log.Warn().Str("path", path).Msg("locate plugin failed")
return nil, nil return nil, nil
@@ -100,21 +100,21 @@ func initPlugin(path, venv string, logOn bool) (plugin funplugin.IPlugin, err er
return 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") log.Info().Str("path", path).Msg("locate plugin")
// priority: hashicorp plugin (debugtalk.bin > debugtalk.py) > go plugin (debugtalk.so) // priority: hashicorp plugin (debugtalk.bin > debugtalk.py) > go plugin (debugtalk.so)
pluginPath, err = locateFile(path, PluginHashicorpGoBuiltFile) pluginPath, err = LocateFile(path, PluginHashicorpGoBuiltFile)
if err == nil { if err == nil {
return return
} }
pluginPath, err = locateFile(path, PluginPySourceFile) pluginPath, err = LocateFile(path, PluginPySourceFile)
if err == nil { if err == nil {
return return
} }
pluginPath, err = locateFile(path, PluginGoBuiltFile) pluginPath, err = LocateFile(path, PluginGoBuiltFile)
if err == nil { if err == nil {
return return
} }
@@ -122,9 +122,9 @@ func locatePlugin(path string) (pluginPath string, err error) {
return "", errors.New("plugin file not found") 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 // 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) stat, err := os.Stat(startPath)
if os.IsNotExist(err) { if os.IsNotExist(err) {
return "", errors.Wrap(err, "start path not exists") 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 "", 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 // 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) { func GetProjectRootDirPath(path string) (rootDir string, err error) {
pluginPath, err := locatePlugin(path) pluginPath, err := LocatePlugin(path)
if err == nil { if err == nil {
rootDir = filepath.Dir(pluginPath) rootDir = filepath.Dir(pluginPath)
return return
} }
// fix: no debugtalk file in project but having proj.json created by startproject // 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 { if err == nil {
rootDir = filepath.Dir(projPath) rootDir = filepath.Dir(projPath)
return return
+5 -4
View File
@@ -17,13 +17,14 @@ import (
"time" "time"
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
"github.com/httprunner/funplugin"
"github.com/jinzhu/copier" "github.com/jinzhu/copier"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"golang.org/x/net/http2" "golang.org/x/net/http2"
"github.com/httprunner/funplugin"
"github.com/httprunner/httprunner/v5/code" "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/sdk"
"github.com/httprunner/httprunner/v5/internal/version" "github.com/httprunner/httprunner/v5/internal/version"
"github.com/httprunner/httprunner/v5/uixt" "github.com/httprunner/httprunner/v5/uixt"
@@ -281,7 +282,7 @@ func (r *HRPRunner) NewCaseRunner(testcase TestCase) (*CaseRunner, error) {
caseRunner := &CaseRunner{ caseRunner := &CaseRunner{
TestCase: testcase, TestCase: testcase,
hrpRunner: r, hrpRunner: r,
parser: newParser(), parser: NewParser(),
uixtDrivers: make(map[string]*uixt.XTDriver), uixtDrivers: make(map[string]*uixt.XTDriver),
} }
config := testcase.Config.Get() config := testcase.Config.Get()
@@ -296,7 +297,7 @@ func (r *HRPRunner) NewCaseRunner(testcase TestCase) (*CaseRunner, error) {
// load plugin info to testcase config // load plugin info to testcase config
pluginPath := plugin.Path() pluginPath := plugin.Path()
pluginContent, err := readFile(pluginPath) pluginContent, err := builtin.LoadFile(pluginPath)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -407,7 +408,7 @@ func (r *CaseRunner) parseConfig() (parsedConfig *TConfig, err error) {
parsedConfig.WebSocketSetting.checkWebSocket() parsedConfig.WebSocketSetting.checkWebSocket()
// parse testcase config parameters // parse testcase config parameters
parametersIterator, err := r.parser.initParametersIterator(parsedConfig) parametersIterator, err := r.parser.InitParametersIterator(parsedConfig)
if err != nil { if err != nil {
log.Error().Err(err). log.Error().Err(err).
Interface("parameters", parsedConfig.Parameters). Interface("parameters", parsedConfig.Parameters).
+2 -1
View File
@@ -5,9 +5,10 @@ import (
"os" "os"
"time" "time"
"github.com/httprunner/httprunner/v5/uixt"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"github.com/httprunner/httprunner/v5/uixt"
) )
// StepFunction implements IStep interface. // StepFunction implements IStep interface.
+21 -21
View File
@@ -30,13 +30,13 @@ import (
type HTTPMethod string type HTTPMethod string
const ( const (
httpGET HTTPMethod = "GET" HTTP_GET HTTPMethod = "GET"
httpHEAD HTTPMethod = "HEAD" HTTP_HEAD HTTPMethod = "HEAD"
httpPOST HTTPMethod = "POST" HTTP_POST HTTPMethod = "POST"
httpPUT HTTPMethod = "PUT" HTTP_PUT HTTPMethod = "PUT"
httpDELETE HTTPMethod = "DELETE" HTTP_DELETE HTTPMethod = "DELETE"
httpOPTIONS HTTPMethod = "OPTIONS" HTTP_OPTIONS HTTPMethod = "OPTIONS"
httpPATCH HTTPMethod = "PATCH" HTTP_PATCH HTTPMethod = "PATCH"
) )
// Request represents HTTP request data structure. // Request represents HTTP request data structure.
@@ -566,11 +566,11 @@ func (s *StepRequest) Loop(times int) *StepRequest {
// GET makes a HTTP GET request. // GET makes a HTTP GET request.
func (s *StepRequest) GET(url string) *StepRequestWithOptionalArgs { func (s *StepRequest) GET(url string) *StepRequestWithOptionalArgs {
if s.Request != nil { if s.Request != nil {
s.Request.Method = httpGET s.Request.Method = HTTP_GET
s.Request.URL = url s.Request.URL = url
} else { } else {
s.Request = &Request{ s.Request = &Request{
Method: httpGET, Method: HTTP_GET,
URL: url, URL: url,
} }
} }
@@ -582,11 +582,11 @@ func (s *StepRequest) GET(url string) *StepRequestWithOptionalArgs {
// HEAD makes a HTTP HEAD request. // HEAD makes a HTTP HEAD request.
func (s *StepRequest) HEAD(url string) *StepRequestWithOptionalArgs { func (s *StepRequest) HEAD(url string) *StepRequestWithOptionalArgs {
if s.Request != nil { if s.Request != nil {
s.Request.Method = httpHEAD s.Request.Method = HTTP_HEAD
s.Request.URL = url s.Request.URL = url
} else { } else {
s.Request = &Request{ s.Request = &Request{
Method: httpHEAD, Method: HTTP_HEAD,
URL: url, URL: url,
} }
} }
@@ -598,11 +598,11 @@ func (s *StepRequest) HEAD(url string) *StepRequestWithOptionalArgs {
// POST makes a HTTP POST request. // POST makes a HTTP POST request.
func (s *StepRequest) POST(url string) *StepRequestWithOptionalArgs { func (s *StepRequest) POST(url string) *StepRequestWithOptionalArgs {
if s.Request != nil { if s.Request != nil {
s.Request.Method = httpPOST s.Request.Method = HTTP_POST
s.Request.URL = url s.Request.URL = url
} else { } else {
s.Request = &Request{ s.Request = &Request{
Method: httpPOST, Method: HTTP_POST,
URL: url, URL: url,
} }
} }
@@ -614,11 +614,11 @@ func (s *StepRequest) POST(url string) *StepRequestWithOptionalArgs {
// PUT makes a HTTP PUT request. // PUT makes a HTTP PUT request.
func (s *StepRequest) PUT(url string) *StepRequestWithOptionalArgs { func (s *StepRequest) PUT(url string) *StepRequestWithOptionalArgs {
if s.Request != nil { if s.Request != nil {
s.Request.Method = httpPUT s.Request.Method = HTTP_PUT
s.Request.URL = url s.Request.URL = url
} else { } else {
s.Request = &Request{ s.Request = &Request{
Method: httpPUT, Method: HTTP_PUT,
URL: url, URL: url,
} }
} }
@@ -630,11 +630,11 @@ func (s *StepRequest) PUT(url string) *StepRequestWithOptionalArgs {
// DELETE makes a HTTP DELETE request. // DELETE makes a HTTP DELETE request.
func (s *StepRequest) DELETE(url string) *StepRequestWithOptionalArgs { func (s *StepRequest) DELETE(url string) *StepRequestWithOptionalArgs {
if s.Request != nil { if s.Request != nil {
s.Request.Method = httpDELETE s.Request.Method = HTTP_DELETE
s.Request.URL = url s.Request.URL = url
} else { } else {
s.Request = &Request{ s.Request = &Request{
Method: httpDELETE, Method: HTTP_DELETE,
URL: url, URL: url,
} }
} }
@@ -646,11 +646,11 @@ func (s *StepRequest) DELETE(url string) *StepRequestWithOptionalArgs {
// OPTIONS makes a HTTP OPTIONS request. // OPTIONS makes a HTTP OPTIONS request.
func (s *StepRequest) OPTIONS(url string) *StepRequestWithOptionalArgs { func (s *StepRequest) OPTIONS(url string) *StepRequestWithOptionalArgs {
if s.Request != nil { if s.Request != nil {
s.Request.Method = httpOPTIONS s.Request.Method = HTTP_OPTIONS
s.Request.URL = url s.Request.URL = url
} else { } else {
s.Request = &Request{ s.Request = &Request{
Method: httpOPTIONS, Method: HTTP_OPTIONS,
URL: url, URL: url,
} }
} }
@@ -662,11 +662,11 @@ func (s *StepRequest) OPTIONS(url string) *StepRequestWithOptionalArgs {
// PATCH makes a HTTP PATCH request. // PATCH makes a HTTP PATCH request.
func (s *StepRequest) PATCH(url string) *StepRequestWithOptionalArgs { func (s *StepRequest) PATCH(url string) *StepRequestWithOptionalArgs {
if s.Request != nil { if s.Request != nil {
s.Request.Method = httpPATCH s.Request.Method = HTTP_PATCH
s.Request.URL = url s.Request.URL = url
} else { } else {
s.Request = &Request{ s.Request = &Request{
Method: httpPATCH, Method: HTTP_PATCH,
URL: url, URL: url,
} }
} }
-1
View File
@@ -1 +0,0 @@
package hrp
+5 -5
View File
@@ -42,14 +42,14 @@ func (s *StepThinkTime) Run(r *SessionRunner) (*StepResult, error) {
cfg := r.caseRunner.Config.Get().ThinkTimeSetting cfg := r.caseRunner.Config.Get().ThinkTimeSetting
if cfg == nil { if cfg == nil {
cfg = &ThinkTimeConfig{thinkTimeDefault, nil, 0} cfg = &ThinkTimeConfig{ThinkTimeDefault, nil, 0}
} }
var tt time.Duration var tt time.Duration
switch cfg.Strategy { switch cfg.Strategy {
case thinkTimeDefault: case ThinkTimeDefault:
tt = time.Duration(thinkTime.Time*1000) * time.Millisecond tt = time.Duration(thinkTime.Time*1000) * time.Millisecond
case thinkTimeRandomPercentage: case ThinkTimeRandomPercentage:
// e.g. {"min_percentage": 0.5, "max_percentage": 1.5} // e.g. {"min_percentage": 0.5, "max_percentage": 1.5}
m, ok := cfg.Setting.(map[string]float64) m, ok := cfg.Setting.(map[string]float64)
if !ok { 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)) res := builtin.GetRandomNumber(int(thinkTime.Time*m["min_percentage"]*1000), int(thinkTime.Time*m["max_percentage"]*1000))
tt = time.Duration(res) * time.Millisecond tt = time.Duration(res) * time.Millisecond
case thinkTimeMultiply: case ThinkTimeMultiply:
value, ok := cfg.Setting.(float64) // e.g. 0.5 value, ok := cfg.Setting.(float64) // e.g. 0.5
if !ok || value <= 0 { if !ok || value <= 0 {
value = thinkTimeDefaultMultiply value = thinkTimeDefaultMultiply
} }
tt = time.Duration(thinkTime.Time*value*1000) * time.Millisecond tt = time.Duration(thinkTime.Time*value*1000) * time.Millisecond
case thinkTimeIgnore: case ThinkTimeIgnore:
// nothing to do // nothing to do
} }
+45
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()
}
}
+18 -16
View File
@@ -1,15 +1,17 @@
package hrp package tests
import ( import (
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
hrp "github.com/httprunner/httprunner/v5"
) )
func TestLoadTestCases(t *testing.T) { func TestLoadTestCases(t *testing.T) {
// load test cases from folder path // load test cases from folder path
tc := TestCasePath("../examples/demo-with-py-plugin/testcases/") tc := hrp.TestCasePath("../examples/demo-with-py-plugin/testcases/")
testCases, err := LoadTestCases(&tc) testCases, err := hrp.LoadTestCases(&tc)
if !assert.Nil(t, err) { if !assert.Nil(t, err) {
t.Fatal() t.Fatal()
} }
@@ -18,8 +20,8 @@ func TestLoadTestCases(t *testing.T) {
} }
// load test cases from folder path, including sub folders // load test cases from folder path, including sub folders
tc = TestCasePath("../examples/demo-with-py-plugin/") tc = hrp.TestCasePath("../examples/demo-with-py-plugin/")
testCases, err = LoadTestCases(&tc) testCases, err = hrp.LoadTestCases(&tc)
if !assert.Nil(t, err) { if !assert.Nil(t, err) {
t.Fatal() t.Fatal()
} }
@@ -28,8 +30,8 @@ func TestLoadTestCases(t *testing.T) {
} }
// load test cases from single file path // load test cases from single file path
tc = TestCasePath(demoTestCaseWithPluginJSONPath) tc = hrp.TestCasePath(demoTestCaseWithPluginJSONPath)
testCases, err = LoadTestCases(&tc) testCases, err = hrp.LoadTestCases(&tc)
if !assert.Nil(t, err) { if !assert.Nil(t, err) {
t.Fatal() t.Fatal()
} }
@@ -38,10 +40,10 @@ func TestLoadTestCases(t *testing.T) {
} }
// load test cases from TestCase instance // load test cases from TestCase instance
testcase := &TestCase{ testcase := &hrp.TestCase{
Config: NewConfig("TestCase").SetWeight(3), Config: hrp.NewConfig("TestCase").SetWeight(3),
} }
testCases, err = LoadTestCases(testcase) testCases, err = hrp.LoadTestCases(testcase)
if !assert.Nil(t, err) { if !assert.Nil(t, err) {
t.Fatal() t.Fatal()
} }
@@ -50,7 +52,7 @@ func TestLoadTestCases(t *testing.T) {
} }
// load test cases from TestCaseJSON // load test cases from TestCaseJSON
testcaseJSON := TestCaseJSON(` testcaseJSON := hrp.TestCaseJSON(`
{ {
"config":{"name":"TestCaseJSON"}, "config":{"name":"TestCaseJSON"},
"teststeps":[ "teststeps":[
@@ -58,7 +60,7 @@ func TestLoadTestCases(t *testing.T) {
{"name": "step2", "shell":{"string": "ls -l"}} {"name": "step2", "shell":{"string": "ls -l"}}
] ]
}`) }`)
testCases, err = LoadTestCases(&testcaseJSON) testCases, err = hrp.LoadTestCases(&testcaseJSON)
if !assert.Nil(t, err) { if !assert.Nil(t, err) {
t.Fatal() t.Fatal()
} }
@@ -68,13 +70,13 @@ func TestLoadTestCases(t *testing.T) {
} }
func TestLoadCase(t *testing.T) { func TestLoadCase(t *testing.T) {
tcJSON := &TestCaseDef{} tcJSON := &hrp.TestCaseDef{}
tcYAML := &TestCaseDef{} tcYAML := &hrp.TestCaseDef{}
err := LoadFileObject(demoTestCaseWithPluginJSONPath, tcJSON) err := hrp.LoadFileObject(demoTestCaseWithPluginJSONPath, tcJSON)
if !assert.NoError(t, err) { if !assert.NoError(t, err) {
t.Fatal() t.Fatal()
} }
err = LoadFileObject(demoTestCaseWithPluginYAMLPath, tcYAML) err = hrp.LoadFileObject(demoTestCaseWithPluginYAMLPath, tcYAML)
if !assert.NoError(t, err) { if !assert.NoError(t, err) {
t.Fatal() t.Fatal()
} }
+63 -61
View File
@@ -1,22 +1,24 @@
package hrp package tests
import ( import (
"fmt" "fmt"
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
hrp "github.com/httprunner/httprunner/v5"
) )
func TestLoadParameters(t *testing.T) { func TestLoadParameters(t *testing.T) {
testData := []struct { testData := []struct {
configParameters map[string]interface{} configParameters map[string]interface{}
loadedParameters map[string]Parameters loadedParameters map[string]hrp.Parameters
}{ }{
{ {
map[string]interface{}{ map[string]interface{}{
"username-password": fmt.Sprintf("${parameterize(%s/$file)}", hrpExamplesDir), "username-password": fmt.Sprintf("${parameterize(%s/$file)}", hrpExamplesDir),
}, },
map[string]Parameters{ map[string]hrp.Parameters{
"username-password": { "username-password": {
{"username": "test1", "password": "111111"}, {"username": "test1", "password": "111111"},
{"username": "test2", "password": "222222"}, {"username": "test2", "password": "222222"},
@@ -33,7 +35,7 @@ func TestLoadParameters(t *testing.T) {
"user_agent": []interface{}{"iOS/10.1", "iOS/10.2"}, "user_agent": []interface{}{"iOS/10.1", "iOS/10.2"},
"app_version": []interface{}{4.0}, "app_version": []interface{}{4.0},
}, },
map[string]Parameters{ map[string]hrp.Parameters{
"username-password": { "username-password": {
{"username": "test1", "password": "111111"}, {"username": "test1", "password": "111111"},
{"username": "test2", "password": "222222"}, {"username": "test2", "password": "222222"},
@@ -54,7 +56,7 @@ func TestLoadParameters(t *testing.T) {
[]interface{}{"test2", "222222"}, []interface{}{"test2", "222222"},
}, },
}, },
map[string]Parameters{ map[string]hrp.Parameters{
"username-password": { "username-password": {
{"username": "test1", "password": "111111"}, {"username": "test1", "password": "111111"},
{"username": "test2", "password": "222222"}, {"username": "test2", "password": "222222"},
@@ -74,9 +76,9 @@ func TestLoadParameters(t *testing.T) {
variablesMapping := map[string]interface{}{ variablesMapping := map[string]interface{}{
"file": "account.csv", "file": "account.csv",
} }
parser := newParser() parser := hrp.NewParser()
for _, data := range testData { for _, data := range testData {
value, err := parser.loadParameters(data.configParameters, variablesMapping) value, err := parser.LoadParameters(data.configParameters, variablesMapping)
if !assert.Nil(t, err) { if !assert.Nil(t, err) {
t.Fatal() t.Fatal()
} }
@@ -109,9 +111,9 @@ func TestLoadParametersError(t *testing.T) {
}, },
}, },
} }
parser := newParser() parser := hrp.NewParser()
for _, data := range testData { 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) { if !assert.Error(t, err) {
t.Fatal() t.Fatal()
} }
@@ -125,28 +127,28 @@ func TestInitParametersIteratorCount(t *testing.T) {
"app_version": []interface{}{4.0}, // 1 "app_version": []interface{}{4.0}, // 1
} }
testData := []struct { testData := []struct {
cfg *TConfig cfg *hrp.TConfig
expectLimit int expectLimit int
}{ }{
// default, no parameters setting // default, no parameters setting
{ {
&TConfig{ &hrp.TConfig{
Parameters: configParameters, Parameters: configParameters,
ParametersSetting: &TParamsConfig{}, ParametersSetting: &hrp.TParamsConfig{},
}, },
6, // 3 * 2 * 1 6, // 3 * 2 * 1
}, },
{ {
&TConfig{ &hrp.TConfig{
Parameters: configParameters, Parameters: configParameters,
}, },
6, // 3 * 2 * 1 6, // 3 * 2 * 1
}, },
// default equals to set overall parameters pick-order to "sequential" // default equals to set overall parameters pick-order to "sequential"
{ {
&TConfig{ &hrp.TConfig{
Parameters: configParameters, Parameters: configParameters,
ParametersSetting: &TParamsConfig{ ParametersSetting: &hrp.TParamsConfig{
PickOrder: "sequential", PickOrder: "sequential",
}, },
}, },
@@ -154,10 +156,10 @@ func TestInitParametersIteratorCount(t *testing.T) {
}, },
// default equals to set each individual parameters pick-order to "sequential" // default equals to set each individual parameters pick-order to "sequential"
{ {
&TConfig{ &hrp.TConfig{
Parameters: configParameters, Parameters: configParameters,
ParametersSetting: &TParamsConfig{ ParametersSetting: &hrp.TParamsConfig{
Strategies: map[string]iteratorStrategy{ Strategies: map[string]hrp.IteratorStrategy{
"username-password": {Name: "user-info", PickOrder: "sequential"}, "username-password": {Name: "user-info", PickOrder: "sequential"},
"user_agent": {Name: "user-identity", PickOrder: "sequential"}, "user_agent": {Name: "user-identity", PickOrder: "sequential"},
"app_version": {Name: "app-version", PickOrder: "sequential"}, "app_version": {Name: "app-version", PickOrder: "sequential"},
@@ -167,10 +169,10 @@ func TestInitParametersIteratorCount(t *testing.T) {
6, // 3 * 2 * 1 6, // 3 * 2 * 1
}, },
{ {
&TConfig{ &hrp.TConfig{
Parameters: configParameters, Parameters: configParameters,
ParametersSetting: &TParamsConfig{ ParametersSetting: &hrp.TParamsConfig{
Strategies: map[string]iteratorStrategy{ Strategies: map[string]hrp.IteratorStrategy{
"user_agent": {Name: "user-identity", PickOrder: "sequential"}, "user_agent": {Name: "user-identity", PickOrder: "sequential"},
"app_version": {Name: "app-version", 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" // set overall parameters overall pick-order to "random"
// each random parameters only select one item // each random parameters only select one item
{ {
&TConfig{ &hrp.TConfig{
Parameters: configParameters, Parameters: configParameters,
ParametersSetting: &TParamsConfig{ ParametersSetting: &hrp.TParamsConfig{
PickOrder: "random", PickOrder: "random",
}, },
}, },
@@ -193,10 +195,10 @@ func TestInitParametersIteratorCount(t *testing.T) {
// set some individual parameters pick-order to "random" // set some individual parameters pick-order to "random"
// this will override overall strategy // this will override overall strategy
{ {
&TConfig{ &hrp.TConfig{
Parameters: configParameters, Parameters: configParameters,
ParametersSetting: &TParamsConfig{ ParametersSetting: &hrp.TParamsConfig{
Strategies: map[string]iteratorStrategy{ Strategies: map[string]hrp.IteratorStrategy{
"user_agent": {Name: "user-identity", PickOrder: "random"}, "user_agent": {Name: "user-identity", PickOrder: "random"},
}, },
}, },
@@ -204,10 +206,10 @@ func TestInitParametersIteratorCount(t *testing.T) {
3, // 3 * 1 * 1 3, // 3 * 1 * 1
}, },
{ {
&TConfig{ &hrp.TConfig{
Parameters: configParameters, Parameters: configParameters,
ParametersSetting: &TParamsConfig{ ParametersSetting: &hrp.TParamsConfig{
Strategies: map[string]iteratorStrategy{ Strategies: map[string]hrp.IteratorStrategy{
"username-password": {Name: "user-info", PickOrder: "random"}, "username-password": {Name: "user-info", PickOrder: "random"},
}, },
}, },
@@ -217,18 +219,18 @@ func TestInitParametersIteratorCount(t *testing.T) {
// set limit for parameters // set limit for parameters
{ {
&TConfig{ &hrp.TConfig{
Parameters: configParameters, // total: 6 = 3 * 2 * 1 Parameters: configParameters, // total: 6 = 3 * 2 * 1
ParametersSetting: &TParamsConfig{ ParametersSetting: &hrp.TParamsConfig{
Limit: 4, // limit could be less than total Limit: 4, // limit could be less than total
}, },
}, },
4, 4,
}, },
{ {
&TConfig{ &hrp.TConfig{
Parameters: configParameters, // total: 6 = 3 * 2 * 1 Parameters: configParameters, // total: 6 = 3 * 2 * 1
ParametersSetting: &TParamsConfig{ ParametersSetting: &hrp.TParamsConfig{
Limit: 9, // limit could also be greater than total Limit: 9, // limit could also be greater than total
}, },
}, },
@@ -238,20 +240,20 @@ func TestInitParametersIteratorCount(t *testing.T) {
// no parameters // no parameters
// also will generate one empty item // also will generate one empty item
{ {
&TConfig{ &hrp.TConfig{
Parameters: nil, Parameters: nil,
ParametersSetting: nil, ParametersSetting: nil,
}, },
1, 1,
}, },
} }
parser := newParser() parser := hrp.NewParser()
for _, data := range testData { for _, data := range testData {
iterator, err := parser.initParametersIterator(data.cfg) iterator, err := parser.InitParametersIterator(data.cfg)
if !assert.Nil(t, err) { if !assert.Nil(t, err) {
t.Fatal() t.Fatal()
} }
if !assert.Equal(t, data.expectLimit, iterator.limit) { if !assert.Equal(t, data.expectLimit, iterator.Limit) {
t.Fatal() t.Fatal()
} }
@@ -275,34 +277,34 @@ func TestInitParametersIteratorUnlimitedCount(t *testing.T) {
"app_version": []interface{}{4.0}, // 1 "app_version": []interface{}{4.0}, // 1
} }
testData := []struct { testData := []struct {
cfg *TConfig cfg *hrp.TConfig
}{ }{
// default, no parameters setting // default, no parameters setting
{ {
&TConfig{ &hrp.TConfig{
Parameters: configParameters, Parameters: configParameters,
ParametersSetting: &TParamsConfig{}, ParametersSetting: &hrp.TParamsConfig{},
}, },
}, },
// no parameters // no parameters
// also will generate one empty item // also will generate one empty item
{ {
&TConfig{ &hrp.TConfig{
Parameters: nil, Parameters: nil,
ParametersSetting: nil, ParametersSetting: nil,
}, },
}, },
} }
parser := newParser() parser := hrp.NewParser()
for _, data := range testData { for _, data := range testData {
iterator, err := parser.initParametersIterator(data.cfg) iterator, err := parser.InitParametersIterator(data.cfg)
if !assert.Nil(t, err) { if !assert.Nil(t, err) {
t.Fatal() t.Fatal()
} }
// set unlimited mode // set unlimited mode
iterator.SetUnlimitedMode() iterator.SetUnlimitedMode()
if !assert.Equal(t, -1, iterator.limit) { if !assert.Equal(t, -1, iterator.Limit) {
t.Fatal() t.Fatal()
} }
@@ -312,7 +314,7 @@ func TestInitParametersIteratorUnlimitedCount(t *testing.T) {
} }
iterator.Next() // consume next parameters iterator.Next() // consume next parameters
} }
if !assert.Equal(t, 100, iterator.index) { if !assert.Equal(t, 100, iterator.Index) {
t.Fatal() t.Fatal()
} }
// should also have next // should also have next
@@ -329,13 +331,13 @@ func TestInitParametersIteratorContent(t *testing.T) {
"app_version": []interface{}{4.0}, // 1 "app_version": []interface{}{4.0}, // 1
} }
testData := []struct { testData := []struct {
cfg *TConfig cfg *hrp.TConfig
checkIndex int checkIndex int
expectParameters map[string]interface{} expectParameters map[string]interface{}
}{ }{
// default, no parameters setting // default, no parameters setting
{ {
&TConfig{ &hrp.TConfig{
Parameters: configParameters, Parameters: configParameters,
}, },
0, // check first item 0, // check first item
@@ -346,16 +348,16 @@ func TestInitParametersIteratorContent(t *testing.T) {
// set limit for parameters // set limit for parameters
{ {
&TConfig{ &hrp.TConfig{
Parameters: map[string]interface{}{ Parameters: map[string]interface{}{
"username-password": []map[string]interface{}{ // 1 "username-password": []map[string]interface{}{ // 1
{"username": "test1", "password": 111111, "other": "111"}, {"username": "test1", "password": 111111, "other": "111"},
}, },
"user_agent": []string{"iOS/10.1", "iOS/10.2"}, // 2 "user_agent": []string{"iOS/10.1", "iOS/10.2"}, // 2
}, },
ParametersSetting: &TParamsConfig{ ParametersSetting: &hrp.TParamsConfig{
Limit: 5, // limit could also be greater than total 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"}, "username-password": {Name: "user-info", PickOrder: "random"},
}, },
}, },
@@ -369,7 +371,7 @@ func TestInitParametersIteratorContent(t *testing.T) {
// no parameters // no parameters
// also will generate one empty item // also will generate one empty item
{ {
&TConfig{ &hrp.TConfig{
Parameters: nil, Parameters: nil,
ParametersSetting: nil, ParametersSetting: nil,
}, },
@@ -377,9 +379,9 @@ func TestInitParametersIteratorContent(t *testing.T) {
map[string]interface{}{}, map[string]interface{}{},
}, },
} }
parser := newParser() parser := hrp.NewParser()
for _, data := range testData { for _, data := range testData {
iterator, err := parser.initParametersIterator(data.cfg) iterator, err := parser.InitParametersIterator(data.cfg)
if !assert.Nil(t, err) { if !assert.Nil(t, err) {
t.Fatal() t.Fatal()
} }
@@ -401,11 +403,11 @@ func TestInitParametersIteratorContent(t *testing.T) {
func TestGenCartesianProduct(t *testing.T) { func TestGenCartesianProduct(t *testing.T) {
testData := []struct { testData := []struct {
multiParameters []Parameters multiParameters []hrp.Parameters
expect Parameters expect hrp.Parameters
}{ }{
{ {
[]Parameters{ []hrp.Parameters{
{ {
{"app_version": 4.0}, {"app_version": 4.0},
}, },
@@ -418,7 +420,7 @@ func TestGenCartesianProduct(t *testing.T) {
{"user_agent": "iOS/10.2"}, {"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.1", "username": "test1"},
{"app_version": 4.0, "password": "111111", "user_agent": "iOS/10.2", "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"}, {"app_version": 4.0, "password": "222222", "user_agent": "iOS/10.1", "username": "test2"},
@@ -430,13 +432,13 @@ func TestGenCartesianProduct(t *testing.T) {
nil, nil,
}, },
{ {
[]Parameters{}, []hrp.Parameters{},
nil, nil,
}, },
} }
for _, data := range testData { for _, data := range testData {
parameters := genCartesianProduct(data.multiParameters) parameters := hrp.GenCartesianProduct(data.multiParameters)
if !assert.Equal(t, data.expect, parameters) { if !assert.Equal(t, data.expect, parameters) {
t.Fatal() t.Fatal()
} }
@@ -490,7 +492,7 @@ func TestConvertParameters(t *testing.T) {
} }
for _, data := range testData { for _, data := range testData {
value, err := convertParameters(data.key, data.parametersRawList) value, err := hrp.ConvertParameters(data.key, data.parametersRawList)
if !assert.Nil(t, err) { if !assert.Nil(t, err) {
t.Fatal() t.Fatal()
} }
@@ -530,7 +532,7 @@ func TestConvertParametersError(t *testing.T) {
} }
for _, data := range testData { for _, data := range testData {
_, err := convertParameters(data.key, data.parametersRawList) _, err := hrp.ConvertParameters(data.key, data.parametersRawList)
if !assert.Error(t, err) { if !assert.Error(t, err) {
t.Fatal() t.Fatal()
} }
+10 -8
View File
@@ -1,43 +1,45 @@
package hrp package tests
import ( import (
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
hrp "github.com/httprunner/httprunner/v5"
) )
func TestLocateFile(t *testing.T) { func TestLocateFile(t *testing.T) {
// specify target file path // 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) { if !assert.Nil(t, err) {
t.Fatal() t.Fatal()
} }
// specify path with the same dir // 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) { if !assert.Nil(t, err) {
t.Fatal() t.Fatal()
} }
// specify target file path dir // specify target file path dir
_, err = locateFile(tmpl("plugin/"), PluginGoSourceFile) _, err = hrp.LocateFile(tmpl("plugin/"), hrp.PluginGoSourceFile)
if !assert.Nil(t, err) { if !assert.Nil(t, err) {
t.Fatal() t.Fatal()
} }
// specify wrong path // specify wrong path
_, err = locateFile(".", PluginGoSourceFile) _, err = hrp.LocateFile(".", hrp.PluginGoSourceFile)
if !assert.Error(t, err) { if !assert.Error(t, err) {
t.Fatal() t.Fatal()
} }
_, err = locateFile("/abc", PluginGoSourceFile) _, err = hrp.LocateFile("/abc", hrp.PluginGoSourceFile)
if !assert.Error(t, err) { if !assert.Error(t, err) {
t.Fatal() t.Fatal()
} }
} }
func TestLocatePythonPlugin(t *testing.T) { func TestLocatePythonPlugin(t *testing.T) {
_, err := locatePlugin(tmpl("plugin/debugtalk.py")) _, err := hrp.LocatePlugin(tmpl("plugin/debugtalk.py"))
if !assert.Nil(t, err) { if !assert.Nil(t, err) {
t.Fatal() t.Fatal()
} }
@@ -47,7 +49,7 @@ func TestLocateGoPlugin(t *testing.T) {
buildHashicorpGoPlugin() buildHashicorpGoPlugin()
defer removeHashicorpGoPlugin() defer removeHashicorpGoPlugin()
_, err := locatePlugin(tmpl("debugtalk.bin")) _, err := hrp.LocatePlugin(tmpl("debugtalk.bin"))
if !assert.Nil(t, err) { if !assert.Nil(t, err) {
t.Fatal() t.Fatal()
} }
+66 -68
View File
@@ -1,22 +1,22 @@
package hrp package tests
import ( import (
"errors" "errors"
"fmt" "fmt"
"os" "os"
"path/filepath"
"testing" "testing"
"time" "time"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
hrp "github.com/httprunner/httprunner/v5"
"github.com/httprunner/httprunner/v5/code" "github.com/httprunner/httprunner/v5/code"
) )
func buildHashicorpGoPlugin() { func buildHashicorpGoPlugin() {
log.Info().Msg("[init] build hashicorp go plugin") 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 { if err != nil {
log.Error().Err(err).Msg("build hashicorp go plugin failed") log.Error().Err(err).Msg("build hashicorp go plugin failed")
os.Exit(code.GetErrorCode(err)) os.Exit(code.GetErrorCode(err))
@@ -26,8 +26,6 @@ func buildHashicorpGoPlugin() {
func removeHashicorpGoPlugin() { func removeHashicorpGoPlugin() {
log.Info().Msg("[teardown] remove hashicorp go plugin") log.Info().Msg("[teardown] remove hashicorp go plugin")
os.Remove(tmpl("debugtalk.bin")) os.Remove(tmpl("debugtalk.bin"))
pluginPath, _ := filepath.Abs(tmpl("debugtalk.bin"))
pluginMap.Delete(pluginPath)
} }
func buildHashicorpPyPlugin() { func buildHashicorpPyPlugin() {
@@ -43,8 +41,8 @@ func buildHashicorpPyPlugin() {
func removeHashicorpPyPlugin() { func removeHashicorpPyPlugin() {
log.Info().Msg("[teardown] remove hashicorp python plugin") log.Info().Msg("[teardown] remove hashicorp python plugin")
// on v4.1^, running case will generate .debugtalk_gen.py used by python plugin // on v4.1^, running case will generate .debugtalk_gen.py used by python plugin
os.Remove(tmpl(PluginPySourceFile)) os.Remove(tmpl(hrp.PluginPySourceFile))
os.Remove(tmpl(PluginPySourceGenFile)) os.Remove(tmpl(hrp.PluginPySourceGenFile))
} }
func TestRunCaseWithGoPlugin(t *testing.T) { func TestRunCaseWithGoPlugin(t *testing.T) {
@@ -62,21 +60,21 @@ func TestRunCaseWithPythonPlugin(t *testing.T) {
} }
func assertRunTestCases(t *testing.T) { func assertRunTestCases(t *testing.T) {
refCase := TestCasePath(demoTestCaseWithPluginJSONPath) refCase := hrp.TestCasePath(demoTestCaseWithPluginJSONPath)
testcase1 := &TestCase{ testcase1 := &hrp.TestCase{
Config: NewConfig("TestCase1"). Config: hrp.NewConfig("TestCase1").
SetBaseURL("https://postman-echo.com"), SetBaseURL("https://postman-echo.com"),
TestSteps: []IStep{ TestSteps: []hrp.IStep{
NewStep("testcase1-step1"). hrp.NewStep("testcase1-step1").
GET("/headers"). GET("/headers").
Validate(). Validate().
AssertEqual("status_code", 200, "check status code"). AssertEqual("status_code", 200, "check status code").
AssertEqual("headers.\"Content-Type\"", "application/json; charset=utf-8", "check http response Content-Type"), AssertEqual("headers.\"Content-Type\"", "application/json; charset=utf-8", "check http response Content-Type"),
NewStep("testcase1-step2").CallRefCase( hrp.NewStep("testcase1-step2").CallRefCase(
&TestCase{ &hrp.TestCase{
Config: NewConfig("testcase1-step3-ref-case").SetBaseURL("https://postman-echo.com"), Config: hrp.NewConfig("testcase1-step3-ref-case").SetBaseURL("https://postman-echo.com"),
TestSteps: []IStep{ TestSteps: []hrp.IStep{
NewStep("ip"). hrp.NewStep("ip").
GET("/ip"). GET("/ip").
Validate(). Validate().
AssertEqual("status_code", 200, "check status code"). 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{ testcase2 := &hrp.TestCase{
Config: NewConfig("TestCase2").SetWeight(3), Config: hrp.NewConfig("TestCase2").SetWeight(3),
} }
r := NewRunner(t) r := hrp.NewRunner(t)
r.SetPluginLogOn() r.SetPluginLogOn()
err := r.Run(testcase1, testcase2) err := r.Run(testcase1, testcase2)
if err != nil { if err != nil {
@@ -103,46 +101,46 @@ func TestRunCaseWithThinkTime(t *testing.T) {
buildHashicorpGoPlugin() buildHashicorpGoPlugin()
defer removeHashicorpGoPlugin() defer removeHashicorpGoPlugin()
testcases := []*TestCase{ testcases := []*hrp.TestCase{
{ {
Config: NewConfig("TestCase1"), Config: hrp.NewConfig("TestCase1"),
TestSteps: []IStep{ TestSteps: []hrp.IStep{
NewStep("thinkTime").SetThinkTime(2), hrp.NewStep("thinkTime").SetThinkTime(2),
}, },
}, },
{ {
Config: NewConfig("TestCase2"). Config: hrp.NewConfig("TestCase2").
SetThinkTime(thinkTimeIgnore, nil, 0), SetThinkTime(hrp.ThinkTimeIgnore, nil, 0),
TestSteps: []IStep{ TestSteps: []hrp.IStep{
NewStep("thinkTime").SetThinkTime(0.5), hrp.NewStep("thinkTime").SetThinkTime(0.5),
}, },
}, },
{ {
Config: NewConfig("TestCase3"). Config: hrp.NewConfig("TestCase3").
SetThinkTime(thinkTimeRandomPercentage, nil, 0), SetThinkTime(hrp.ThinkTimeRandomPercentage, nil, 0),
TestSteps: []IStep{ TestSteps: []hrp.IStep{
NewStep("thinkTime").SetThinkTime(1), hrp.NewStep("thinkTime").SetThinkTime(1),
}, },
}, },
{ {
Config: NewConfig("TestCase4"). Config: hrp.NewConfig("TestCase4").
SetThinkTime(thinkTimeRandomPercentage, map[string]interface{}{"min_percentage": 2, "max_percentage": 3}, 2.5), SetThinkTime(hrp.ThinkTimeRandomPercentage, map[string]interface{}{"min_percentage": 2, "max_percentage": 3}, 2.5),
TestSteps: []IStep{ TestSteps: []hrp.IStep{
NewStep("thinkTime").SetThinkTime(1), hrp.NewStep("thinkTime").SetThinkTime(1),
}, },
}, },
{ {
Config: NewConfig("TestCase5"), Config: hrp.NewConfig("TestCase5"),
TestSteps: []IStep{ TestSteps: []hrp.IStep{
// think time: 3s, random pct: {"min_percentage":1, "max_percentage":1.5}, limit: 4s // 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} expectedMinValue := []float64{2, 0, 0.5, 2, 3}
expectedMaxValue := []float64{2.5, 0.5, 2, 3, 10} expectedMaxValue := []float64{2.5, 0.5, 2, 3, 10}
for idx, testcase := range testcases { for idx, testcase := range testcases {
r := NewRunner(t) r := hrp.NewRunner(t)
startTime := time.Now() startTime := time.Now()
err := r.Run(testcase) err := r.Run(testcase)
if err != nil { if err != nil {
@@ -158,20 +156,20 @@ func TestRunCaseWithThinkTime(t *testing.T) {
} }
func TestRunCaseWithShell(t *testing.T) { func TestRunCaseWithShell(t *testing.T) {
testcase1 := &TestCase{ testcase1 := &hrp.TestCase{
Config: NewConfig("complex shell with env variables"). Config: hrp.NewConfig("complex shell with env variables").
WithVariables(map[string]interface{}{ WithVariables(map[string]interface{}{
"SS": "12345", "SS": "12345",
"ABC": "$SS", "ABC": "$SS",
}), }),
TestSteps: []IStep{ TestSteps: []hrp.IStep{
NewStep("shell21").Shell("echo hello world"), hrp.NewStep("shell21").Shell("echo hello world"),
// NewStep("shell21").Shell("echo $ABC"), // NewStep("shell21").Shell("echo $ABC"),
// NewStep("shell21").Shell("which hrp"), // NewStep("shell21").Shell("which hrp"),
}, },
} }
r := NewRunner(t) r := hrp.NewRunner(t)
err := r.Run(testcase1) err := r.Run(testcase1)
if err != nil { if err != nil {
t.Fatal() t.Fatal()
@@ -193,16 +191,16 @@ func TestRunCaseWithFunction(t *testing.T) {
err3 = errors.New("func3 error") err3 = errors.New("func3 error")
fmt.Println("call function3 with return value and error") fmt.Println("call function3 with return value and error")
} }
testcase1 := &TestCase{ testcase1 := &hrp.TestCase{
Config: NewConfig("call function"), Config: hrp.NewConfig("call function"),
TestSteps: []IStep{ TestSteps: []hrp.IStep{
NewStep("fn1").Function(fn1), hrp.NewStep("fn1").Function(fn1),
NewStep("fn2").Function(fn2), hrp.NewStep("fn2").Function(fn2),
NewStep("fn3").Function(fn3), hrp.NewStep("fn3").Function(fn3),
}, },
} }
r := NewRunner(t) r := hrp.NewRunner(t)
err := r.Run(testcase1) err := r.Run(testcase1)
if err != nil { if err != nil {
t.Fatal() t.Fatal()
@@ -220,8 +218,8 @@ func TestRunCaseWithPluginJSON(t *testing.T) {
buildHashicorpGoPlugin() buildHashicorpGoPlugin()
defer removeHashicorpGoPlugin() defer removeHashicorpGoPlugin()
testCase := TestCasePath(demoTestCaseWithPluginJSONPath) testCase := hrp.TestCasePath(demoTestCaseWithPluginJSONPath)
err := NewRunner(nil).Run(&testCase) // hrp.Run(testCase) err := hrp.NewRunner(nil).Run(&testCase) // hrp.Run(testCase)
if err != nil { if err != nil {
t.Fatal() t.Fatal()
} }
@@ -243,22 +241,22 @@ func TestRunCaseWithRefAPI(t *testing.T) {
buildHashicorpGoPlugin() buildHashicorpGoPlugin()
defer removeHashicorpGoPlugin() defer removeHashicorpGoPlugin()
testCase := TestCasePath(demoTestCaseWithRefAPIPath) testCase := hrp.TestCasePath(demoTestCaseWithRefAPIPath)
err := NewRunner(nil).Run(&testCase) err := hrp.NewRunner(nil).Run(&testCase)
if err != nil { if err != nil {
t.Fatal() t.Fatal()
} }
refAPI := APIPath(demoAPIGETPath) refAPI := hrp.APIPath(demoAPIGETPath)
testcase := &TestCase{ testcase := &hrp.TestCase{
Config: NewConfig("TestCase"). Config: hrp.NewConfig("TestCase").
SetBaseURL("https://postman-echo.com"), SetBaseURL("https://postman-echo.com"),
TestSteps: []IStep{ TestSteps: []hrp.IStep{
NewStep("run referenced api").CallRefAPI(&refAPI), hrp.NewStep("run referenced api").CallRefAPI(&refAPI),
}, },
} }
r := NewRunner(t) r := hrp.NewRunner(t)
err = r.Run(testcase) err = r.Run(testcase)
if err != nil { if err != nil {
t.Fatal() t.Fatal()
@@ -266,15 +264,15 @@ func TestRunCaseWithRefAPI(t *testing.T) {
} }
func TestSessionRunner(t *testing.T) { func TestSessionRunner(t *testing.T) {
testcase := TestCase{ testcase := hrp.TestCase{
Config: NewConfig("TestCase"). Config: hrp.NewConfig("TestCase").
WithVariables(map[string]interface{}{ WithVariables(map[string]interface{}{
"a": 12.3, "a": 12.3,
"b": 3.45, "b": 3.45,
"varFoo": "${max($a, $b)}", "varFoo": "${max($a, $b)}",
}), }),
TestSteps: []IStep{ TestSteps: []hrp.IStep{
NewStep("check variables"). hrp.NewStep("check variables").
WithVariables(map[string]interface{}{ WithVariables(map[string]interface{}{
"a": 12.3, "a": 12.3,
"b": 34.5, "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() sessionRunner := caseRunner.NewSession()
step := testcase.TestSteps[0] step := testcase.TestSteps[0]
if !assert.Equal(t, step.Config().Variables["varFoo"], "${max($a, $b)}") { if !assert.Equal(t, step.Config().Variables["varFoo"], "${max($a, $b)}") {
@@ -1,40 +1,42 @@
package hrp package tests
import ( import (
"math" "math"
"testing" "testing"
hrp "github.com/httprunner/httprunner/v5"
) )
func TestRunCaseWithRendezvous(t *testing.T) { func TestRunCaseWithRendezvous(t *testing.T) {
rendezvousBoundaryTestcase := &TestCase{ rendezvousBoundaryTestcase := &hrp.TestCase{
Config: NewConfig("run request with functions"). Config: hrp.NewConfig("run request with functions").
SetBaseURL("https://postman-echo.com"). SetBaseURL("https://postman-echo.com").
WithVariables(map[string]interface{}{ WithVariables(map[string]interface{}{
"n": 5, "n": 5,
"a": 12.3, "a": 12.3,
"b": 3.45, "b": 3.45,
}), }),
TestSteps: []IStep{ TestSteps: []hrp.IStep{
NewStep("test negative number"). hrp.NewStep("test negative number").
SetRendezvous("test negative number"). SetRendezvous("test negative number").
WithUserNumber(-1), WithUserNumber(-1),
NewStep("test overflow number"). hrp.NewStep("test overflow number").
SetRendezvous("test overflow number"). SetRendezvous("test overflow number").
WithUserNumber(1000000), WithUserNumber(1000000),
NewStep("test negative percent"). hrp.NewStep("test negative percent").
SetRendezvous("test very low percent"). SetRendezvous("test very low percent").
WithUserPercent(-0.5), WithUserPercent(-0.5),
NewStep("test very low percent"). hrp.NewStep("test very low percent").
SetRendezvous("test very low percent"). SetRendezvous("test very low percent").
WithUserPercent(0.00001), WithUserPercent(0.00001),
NewStep("test overflow percent"). hrp.NewStep("test overflow percent").
SetRendezvous("test overflow percent"). SetRendezvous("test overflow percent").
WithUserPercent(1.5), WithUserPercent(1.5),
NewStep("test conflict params"). hrp.NewStep("test conflict params").
SetRendezvous("test conflict params"). SetRendezvous("test conflict params").
WithUserNumber(1). WithUserNumber(1).
WithUserPercent(0.123), WithUserPercent(0.123),
NewStep("test negative timeout"). hrp.NewStep("test negative timeout").
SetRendezvous("test negative timeout"). SetRendezvous("test negative timeout").
WithTimeout(-1000), WithTimeout(-1000),
}, },
@@ -55,7 +57,7 @@ func TestRunCaseWithRendezvous(t *testing.T) {
{number: 100, percent: 1, timeout: 5000}, {number: 100, percent: 1, timeout: 5000},
} }
rendezvousList := InitRendezvous(rendezvousBoundaryTestcase, 100) rendezvousList := hrp.InitRendezvous(rendezvousBoundaryTestcase, 100)
for i, r := range rendezvousList { for i, r := range rendezvousList {
if r.Number != expectedRendezvousParams[i].number { if r.Number != expectedRendezvousParams[i].number {
@@ -1,17 +1,15 @@
package hrp package tests
import ( import (
"io"
"net/http"
"strings"
"testing" "testing"
"time" "time"
hrp "github.com/httprunner/httprunner/v5"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
var ( var (
stepGET = NewStep("get with params"). stepGET = hrp.NewStep("get with params").
GET("/get"). GET("/get").
WithParams(map[string]interface{}{"foo1": "bar1", "foo2": "bar2"}). WithParams(map[string]interface{}{"foo1": "bar1", "foo2": "bar2"}).
WithHeaders(map[string]string{"User-Agent": "HttpRunnerPlus"}). 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("headers.\"Content-Type\"", "application/json; charset=utf-8", "check header Content-Type").
AssertEqual("body.args.foo1", "bar1", "check param foo1"). AssertEqual("body.args.foo1", "bar1", "check param foo1").
AssertEqual("body.args.foo2", "bar2", "check param foo2") AssertEqual("body.args.foo2", "bar2", "check param foo2")
stepPOSTData = NewStep("post form data"). stepPOSTData = hrp.NewStep("post form data").
POST("/post"). POST("/post").
WithParams(map[string]interface{}{"foo1": "bar1", "foo2": "bar2"}). WithParams(map[string]interface{}{"foo1": "bar1", "foo2": "bar2"}).
WithHeaders(map[string]string{"User-Agent": "HttpRunnerPlus", "Content-Type": "application/x-www-form-urlencoded"}). WithHeaders(map[string]string{"User-Agent": "HttpRunnerPlus", "Content-Type": "application/x-www-form-urlencoded"}).
@@ -33,7 +31,7 @@ var (
func TestRunRequestGetToStruct(t *testing.T) { func TestRunRequestGetToStruct(t *testing.T) {
tStep := stepGET tStep := stepGET
if tStep.Request.Method != httpGET { if tStep.Request.Method != hrp.HTTP_GET {
t.Fatalf("tStep.Request.Method != GET") t.Fatalf("tStep.Request.Method != GET")
} }
if tStep.Request.URL != "/get" { if tStep.Request.URL != "/get" {
@@ -48,7 +46,7 @@ func TestRunRequestGetToStruct(t *testing.T) {
if tStep.Request.Cookies["user"] != "debugtalk" { if tStep.Request.Cookies["user"] != "debugtalk" {
t.Fatalf("tStep.Request.Cookies mismatch") 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 { if !ok || validator.Check != "status_code" || validator.Expect != 200 {
t.Fatalf("tStep.Validators mismatch") t.Fatalf("tStep.Validators mismatch")
} }
@@ -56,7 +54,7 @@ func TestRunRequestGetToStruct(t *testing.T) {
func TestRunRequestPostDataToStruct(t *testing.T) { func TestRunRequestPostDataToStruct(t *testing.T) {
tStep := stepPOSTData tStep := stepPOSTData
if tStep.Request.Method != httpPOST { if tStep.Request.Method != hrp.HTTP_POST {
t.Fatalf("tStep.Request.Method != POST") t.Fatalf("tStep.Request.Method != POST")
} }
if tStep.Request.URL != "/post" { if tStep.Request.URL != "/post" {
@@ -74,18 +72,18 @@ func TestRunRequestPostDataToStruct(t *testing.T) {
if tStep.Request.Body != "a=1&b=2" { if tStep.Request.Body != "a=1&b=2" {
t.Fatalf("tStep.Request.Data mismatch") 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 { if !ok || validator.Check != "status_code" || validator.Expect != 200 {
t.Fatalf("tStep.Validators mismatch") t.Fatalf("tStep.Validators mismatch")
} }
} }
func TestRunRequestStatOn(t *testing.T) { func TestRunRequestStatOn(t *testing.T) {
testcase := TestCase{ testcase := hrp.TestCase{
Config: NewConfig("test").SetBaseURL("https://postman-echo.com"), Config: hrp.NewConfig("test").SetBaseURL("https://postman-echo.com"),
TestSteps: []IStep{stepGET, stepPOSTData}, TestSteps: []hrp.IStep{stepGET, stepPOSTData},
} }
caseRunner, _ := NewRunner(t).SetHTTPStatOn().NewCaseRunner(testcase) caseRunner, _ := hrp.NewRunner(t).SetHTTPStatOn().NewCaseRunner(testcase)
sessionRunner := caseRunner.NewSession() sessionRunner := caseRunner.NewSession()
summary, err := sessionRunner.Start(nil) summary, err := sessionRunner.Start(nil)
if err != nil { if err != nil {
@@ -162,15 +160,15 @@ func TestRunRequestStatOn(t *testing.T) {
} }
func TestRunCaseWithTimeout(t *testing.T) { func TestRunCaseWithTimeout(t *testing.T) {
r := NewRunner(t) r := hrp.NewRunner(t)
// global timeout // global timeout
testcase1 := &TestCase{ testcase1 := &hrp.TestCase{
Config: NewConfig("TestCase1"). Config: hrp.NewConfig("TestCase1").
SetRequestTimeout(10). // set global timeout to 10s SetRequestTimeout(10). // set global timeout to 10s
SetBaseURL("https://postman-echo.com"), SetBaseURL("https://postman-echo.com"),
TestSteps: []IStep{ TestSteps: []hrp.IStep{
NewStep("step1"). hrp.NewStep("step1").
GET("/delay/1"). GET("/delay/1").
Validate(). Validate().
AssertEqual("status_code", 200, "check status code"), AssertEqual("status_code", 200, "check status code"),
@@ -181,12 +179,12 @@ func TestRunCaseWithTimeout(t *testing.T) {
t.FailNow() t.FailNow()
} }
testcase2 := &TestCase{ testcase2 := &hrp.TestCase{
Config: NewConfig("TestCase2"). Config: hrp.NewConfig("TestCase2").
SetRequestTimeout(5). // set global timeout to 10s SetRequestTimeout(5). // set global timeout to 10s
SetBaseURL("https://postman-echo.com"), SetBaseURL("https://postman-echo.com"),
TestSteps: []IStep{ TestSteps: []hrp.IStep{
NewStep("step1"). hrp.NewStep("step1").
GET("/delay/10"). GET("/delay/10").
Validate(). Validate().
AssertEqual("status_code", 200, "check status code"), AssertEqual("status_code", 200, "check status code"),
@@ -198,12 +196,12 @@ func TestRunCaseWithTimeout(t *testing.T) {
} }
// step timeout // step timeout
testcase3 := &TestCase{ testcase3 := &hrp.TestCase{
Config: NewConfig("TestCase3"). Config: hrp.NewConfig("TestCase3").
SetRequestTimeout(10). SetRequestTimeout(10).
SetBaseURL("https://postman-echo.com"), SetBaseURL("https://postman-echo.com"),
TestSteps: []IStep{ TestSteps: []hrp.IStep{
NewStep("step2"). hrp.NewStep("step2").
GET("/delay/11"). GET("/delay/11").
SetTimeout(15*time.Second). // set step timeout to 4s SetTimeout(15*time.Second). // set step timeout to 4s
Validate(). Validate().
@@ -215,70 +213,3 @@ func TestRunCaseWithTimeout(t *testing.T) {
t.FailNow() 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()
}
}
}
+28 -27
View File
@@ -1,87 +1,88 @@
//go:build localtest //go:build localtest
package hrp package tests
import ( import (
"testing" "testing"
hrp "github.com/httprunner/httprunner/v5"
"github.com/httprunner/httprunner/v5/uixt/option" "github.com/httprunner/httprunner/v5/uixt/option"
) )
func TestIOSSettingsAction(t *testing.T) { func TestIOSSettingsAction(t *testing.T) {
testCase := &TestCase{ testCase := &hrp.TestCase{
Config: NewConfig("ios ui action on Settings"). Config: hrp.NewConfig("ios ui action on Settings").
SetIOS(option.WithWDAPort(8700), option.WithWDAMjpegPort(8800)), SetIOS(option.WithWDAPort(8700), option.WithWDAMjpegPort(8800)),
TestSteps: []IStep{ TestSteps: []hrp.IStep{
NewStep("launch Settings"). hrp.NewStep("launch Settings").
IOS().Home().TapByOCR("设置"). IOS().Home().TapByOCR("设置").
Validate(). Validate().
AssertNameExists("飞行模式"). AssertNameExists("飞行模式").
AssertLabelExists("蓝牙"). AssertLabelExists("蓝牙").
AssertOCRExists("个人热点"), AssertOCRExists("个人热点"),
NewStep("swipe up and down"). hrp.NewStep("swipe up and down").
IOS().SwipeUp().SwipeUp().SwipeDown(), IOS().SwipeUp().SwipeUp().SwipeDown(),
}, },
} }
err := NewRunner(t).Run(testCase) err := hrp.NewRunner(t).Run(testCase)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
} }
func TestIOSSearchApp(t *testing.T) { func TestIOSSearchApp(t *testing.T) {
testCase := &TestCase{ testCase := &hrp.TestCase{
Config: NewConfig("ios ui action on Search App 资源库"), Config: hrp.NewConfig("ios ui action on Search App 资源库"),
TestSteps: []IStep{ TestSteps: []hrp.IStep{
NewStep("进入 App 资源库 搜索框"). hrp.NewStep("进入 App 资源库 搜索框").
IOS().Home().SwipeLeft().SwipeLeft().TapByCV("dewey-search-field"). IOS().Home().SwipeLeft().SwipeLeft().TapByCV("dewey-search-field").
Validate(). Validate().
AssertLabelExists("取消"), AssertLabelExists("取消"),
NewStep("搜索抖音"). hrp.NewStep("搜索抖音").
IOS().Input("抖音\n"), IOS().Input("抖音\n"),
}, },
} }
err := NewRunner(t).Run(testCase) err := hrp.NewRunner(t).Run(testCase)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
} }
func TestIOSAppLaunch(t *testing.T) { func TestIOSAppLaunch(t *testing.T) {
testCase := &TestCase{ testCase := &hrp.TestCase{
Config: NewConfig("启动 & 关闭 App"). Config: hrp.NewConfig("启动 & 关闭 App").
SetIOS(option.WithWDAPort(8700), option.WithWDAMjpegPort(8800)), SetIOS(option.WithWDAPort(8700), option.WithWDAMjpegPort(8800)),
TestSteps: []IStep{ TestSteps: []hrp.IStep{
NewStep("终止今日头条"). hrp.NewStep("终止今日头条").
IOS().AppTerminate("com.ss.iphone.article.News"), IOS().AppTerminate("com.ss.iphone.article.News"),
NewStep("启动今日头条"). hrp.NewStep("启动今日头条").
IOS().AppLaunch("com.ss.iphone.article.News"), IOS().AppLaunch("com.ss.iphone.article.News"),
NewStep("终止今日头条"). hrp.NewStep("终止今日头条").
IOS().AppTerminate("com.ss.iphone.article.News"), IOS().AppTerminate("com.ss.iphone.article.News"),
NewStep("启动今日头条"). hrp.NewStep("启动今日头条").
IOS().AppLaunch("com.ss.iphone.article.News"), IOS().AppLaunch("com.ss.iphone.article.News"),
}, },
} }
err := NewRunner(t).Run(testCase) err := hrp.NewRunner(t).Run(testCase)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
} }
func TestAndroidAction(t *testing.T) { func TestAndroidAction(t *testing.T) {
testCase := &TestCase{ testCase := &hrp.TestCase{
Config: NewConfig("android ui action"), Config: hrp.NewConfig("android ui action"),
TestSteps: []IStep{ TestSteps: []hrp.IStep{
NewStep("launch douyin"). hrp.NewStep("launch douyin").
Android().Serial("xxx").TapByOCR("抖音"). Android().Serial("xxx").TapByOCR("抖音").
Validate(). Validate().
AssertNameExists("首页", "首页 tab 不存在"). AssertNameExists("首页", "首页 tab 不存在").
AssertNameExists("消息", "消息 tab 不存在"), AssertNameExists("消息", "消息 tab 不存在"),
NewStep("swipe up and down"). hrp.NewStep("swipe up and down").
Android().Serial("xxx").SwipeUp().SwipeUp().SwipeDown(), Android().Serial("xxx").SwipeUp().SwipeUp().SwipeDown(),
}, },
} }
err := NewRunner(t).Run(testCase) err := hrp.NewRunner(t).Run(testCase)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
+15 -13
View File
@@ -1,23 +1,25 @@
package hrp package tests
import ( import (
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
hrp "github.com/httprunner/httprunner/v5"
) )
func TestGenHTMLReport(t *testing.T) { func TestGenHTMLReport(t *testing.T) {
summary := NewSummary() summary := hrp.NewSummary()
caseSummary1 := NewCaseSummary() caseSummary1 := hrp.NewCaseSummary()
stepResult1 := &StepResult{} stepResult1 := &hrp.StepResult{}
caseSummary1.AddStepResult(stepResult1) caseSummary1.AddStepResult(stepResult1)
summary.AddCaseSummary(caseSummary1) summary.AddCaseSummary(caseSummary1)
caseSummary2 := NewCaseSummary() caseSummary2 := hrp.NewCaseSummary()
stepResult2 := &StepResult{ stepResult2 := &hrp.StepResult{
Name: "Test", Name: "Test",
StepType: StepTypeRequest, StepType: hrp.StepTypeRequest,
Success: false, Success: false,
ContentSize: 0, ContentSize: 0,
Attachments: "err", Attachments: "err",
@@ -37,22 +39,22 @@ func TestGenHTMLReport(t *testing.T) {
} }
func TestTestCaseSummary_AddStepResult(t *testing.T) { func TestTestCaseSummary_AddStepResult(t *testing.T) {
caseSummary := NewCaseSummary() caseSummary := hrp.NewCaseSummary()
stepResult1 := &StepResult{ stepResult1 := &hrp.StepResult{
Name: "Test1", Name: "Test1",
StepType: StepTypeRequest, StepType: hrp.StepTypeRequest,
Success: true, Success: true,
ContentSize: 0, ContentSize: 0,
Attachments: "err", Attachments: "err",
} }
caseSummary.AddStepResult(stepResult1) caseSummary.AddStepResult(stepResult1)
stepResult2 := &StepResult{ stepResult2 := &hrp.StepResult{
Name: "Test2", Name: "Test2",
StepType: StepTypeTestCase, StepType: hrp.StepTypeTestCase,
Success: false, Success: false,
ContentSize: 0, ContentSize: 0,
Attachments: "err", Attachments: "err",
Data: []*StepResult{stepResult1}, Data: []*hrp.StepResult{stepResult1},
} }
caseSummary.AddStepResult(stepResult2) caseSummary.AddStepResult(stepResult2)
+21 -47
View File
@@ -1,10 +1,10 @@
package hrp package tests
import ( import (
"path/filepath" "path/filepath"
"testing" "testing"
"github.com/stretchr/testify/assert" hrp "github.com/httprunner/httprunner/v5"
) )
const ( const (
@@ -25,10 +25,10 @@ var (
demoAPIGETPath = tmpl("/api/get.yml") 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{ var demoTestCaseWithPlugin = &hrp.TestCase{
Config: NewConfig("demo with complex mechanisms"). Config: hrp.NewConfig("demo with complex mechanisms").
SetBaseURL("https://postman-echo.com"). SetBaseURL("https://postman-echo.com").
WithVariables(map[string]interface{}{ // global level variables WithVariables(map[string]interface{}{ // global level variables
"n": "${sum_ints(1, 2, 2)}", "n": "${sum_ints(1, 2, 2)}",
@@ -37,9 +37,9 @@ var demoTestCaseWithPlugin = &TestCase{
"varFoo1": "${gen_random_string($n)}", "varFoo1": "${gen_random_string($n)}",
"varFoo2": "${max($a, $b)}", // 12.3; eval with built-in function "varFoo2": "${max($a, $b)}", // 12.3; eval with built-in function
}), }),
TestSteps: []IStep{ TestSteps: []hrp.IStep{
NewStep("transaction 1 start").StartTransaction("tran1"), // start transaction hrp.NewStep("transaction 1 start").StartTransaction("tran1"), // start transaction
NewStep("get with params"). hrp.NewStep("get with params").
WithVariables(map[string]interface{}{ // step level variables WithVariables(map[string]interface{}{ // step level variables
"n": 3, // inherit config level variables if not set in step level, a/varFoo1 "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 "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("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 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 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 hrp.NewStep("transaction 1 end").EndTransaction("tran1"), // end transaction
NewStep("post json data"). hrp.NewStep("post json data").
POST("/post"). POST("/post").
WithBody(map[string]interface{}{ WithBody(map[string]interface{}{
"foo1": "$varFoo1", // reference former extracted variable "foo1": "$varFoo1", // reference former extracted variable
@@ -70,7 +70,7 @@ var demoTestCaseWithPlugin = &TestCase{
AssertEqual("status_code", 200, "check status code"). AssertEqual("status_code", 200, "check status code").
AssertLengthEqual("body.json.foo1", 5, "check args foo1"). AssertLengthEqual("body.json.foo1", 5, "check args foo1").
AssertEqual("body.json.foo2", 12.3, "check args foo2"), AssertEqual("body.json.foo2", 12.3, "check args foo2"),
NewStep("post form data"). hrp.NewStep("post form data").
POST("/post"). POST("/post").
WithHeaders(map[string]string{"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"}). WithHeaders(map[string]string{"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"}).
WithBody(map[string]interface{}{ WithBody(map[string]interface{}{
@@ -84,15 +84,15 @@ var demoTestCaseWithPlugin = &TestCase{
AssertEqual("status_code", 200, "check status code"). AssertEqual("status_code", 200, "check status code").
AssertLengthEqual("body.form.foo1", 5, "check args foo1"). AssertLengthEqual("body.form.foo1", 5, "check args foo1").
AssertEqual("body.form.foo2", "12.3", "check args foo2"), // form data will be converted to string 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"}). GET("/get").WithParams(map[string]interface{}{"time": "$varTime"}).
Validate(). Validate().
AssertLengthEqual("body.args.time", 13, "check extracted var timestamp"), AssertLengthEqual("body.args.time", 13, "check extracted var timestamp"),
}, },
} }
var demoTestCaseWithoutPlugin = &TestCase{ var demoTestCaseWithoutPlugin = &hrp.TestCase{
Config: NewConfig("demo without custom function plugin"). Config: hrp.NewConfig("demo without custom function plugin").
SetBaseURL("https://postman-echo.com"). SetBaseURL("https://postman-echo.com").
WithVariables(map[string]interface{}{ // global level variables WithVariables(map[string]interface{}{ // global level variables
"n": 5, "n": 5,
@@ -101,9 +101,9 @@ var demoTestCaseWithoutPlugin = &TestCase{
"varFoo1": "${gen_random_string($n)}", "varFoo1": "${gen_random_string($n)}",
"varFoo2": "${max($a, $b)}", // 12.3; eval with built-in function "varFoo2": "${max($a, $b)}", // 12.3; eval with built-in function
}), }),
TestSteps: []IStep{ TestSteps: []hrp.IStep{
NewStep("transaction 1 start").StartTransaction("tran1"), // start transaction hrp.NewStep("transaction 1 start").StartTransaction("tran1"), // start transaction
NewStep("get with params"). hrp.NewStep("get with params").
WithVariables(map[string]interface{}{ // step level variables WithVariables(map[string]interface{}{ // step level variables
"n": 3, // inherit config level variables if not set in step level, a/varFoo1 "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 "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("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 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 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 hrp.NewStep("transaction 1 end").EndTransaction("tran1"), // end transaction
NewStep("post json data"). hrp.NewStep("post json data").
POST("/post"). POST("/post").
WithBody(map[string]interface{}{ WithBody(map[string]interface{}{
"foo1": "$varFoo1", // reference former extracted variable "foo1": "$varFoo1", // reference former extracted variable
@@ -132,7 +132,7 @@ var demoTestCaseWithoutPlugin = &TestCase{
AssertEqual("status_code", 200, "check status code"). AssertEqual("status_code", 200, "check status code").
AssertLengthEqual("body.json.foo1", 5, "check args foo1"). AssertLengthEqual("body.json.foo1", 5, "check args foo1").
AssertEqual("body.json.foo2", 12.3, "check args foo2"), AssertEqual("body.json.foo2", 12.3, "check args foo2"),
NewStep("post form data"). hrp.NewStep("post form data").
POST("/post"). POST("/post").
WithHeaders(map[string]string{"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"}). WithHeaders(map[string]string{"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"}).
WithBody(map[string]interface{}{ WithBody(map[string]interface{}{
@@ -146,7 +146,7 @@ var demoTestCaseWithoutPlugin = &TestCase{
AssertEqual("status_code", 200, "check status code"). AssertEqual("status_code", 200, "check status code").
AssertLengthEqual("body.form.foo1", 5, "check args foo1"). AssertLengthEqual("body.form.foo1", 5, "check args foo1").
AssertEqual("body.form.foo2", "12.3", "check args foo2"), // form data will be converted to string 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"}). GET("/get").WithParams(map[string]interface{}{"time": "$varTime"}).
Validate(). Validate().
AssertLengthEqual("body.args.time", 13, "check extracted var timestamp"), AssertLengthEqual("body.args.time", 13, "check extracted var timestamp"),
@@ -172,29 +172,3 @@ func TestGenDemoTestCase(t *testing.T) {
t.Fatal() 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()
}
}
}