fix: call referenced api/testcase with relative path

This commit is contained in:
debugtalk
2022-03-27 13:05:20 +08:00
parent cdabde4dc8
commit 41f60d254f
23 changed files with 170 additions and 436 deletions

View File

@@ -25,8 +25,11 @@ jobs:
run: make build run: make build
- name: Run start project - name: Run start project
run: ./output/hrp startproject demo run: ./output/hrp startproject demo
- name: Run demo tests - name: Run generated demo tests
run: ./output/hrp run demo/testcases/demo_with_funplugin.json demo/testcases/demo_requests.yml demo/testcases/demo_ref_testcase.yml run: ./output/hrp run demo/testcases/demo_with_funplugin.json demo/testcases/demo_requests.yml demo/testcases/demo_ref_testcase.yml
- name: Run demo in examples
run: |
./output/hrp run examples/demo-with-py-plugin/testcases/demo_with_funplugin.json
scaffold-with-go-plugin: scaffold-with-go-plugin:
strategy: strategy:
@@ -47,8 +50,12 @@ jobs:
run: make build run: make build
- name: Run start project - name: Run start project
run: ./output/hrp startproject demo --go run: ./output/hrp startproject demo --go
- name: Run demo tests - name: Run generated demo tests
run: ./output/hrp run demo/testcases/demo_with_funplugin.json demo/testcases/demo_requests.yml demo/testcases/demo_ref_testcase.yml run: ./output/hrp run demo/testcases/demo_with_funplugin.json demo/testcases/demo_requests.yml demo/testcases/demo_ref_testcase.yml
- name: Run demo in examples
run: |
go build -o examples/demo-with-go-plugin/debugtalk.bin examples/demo-with-go-plugin/plugin/debugtalk.go
./output/hrp run examples/demo-with-go-plugin/testcases/demo_with_funplugin.json
scaffold-without-custom-plugin: scaffold-without-custom-plugin:
strategy: strategy:
@@ -69,5 +76,8 @@ jobs:
run: make build run: make build
- name: Run start project - name: Run start project
run: ./output/hrp startproject demo --ignore-plugin run: ./output/hrp startproject demo --ignore-plugin
- name: Run demo tests - name: Run generated demo tests
run: ./output/hrp run demo/testcases/demo_without_funplugin.json run: ./output/hrp run demo/testcases/demo_without_funplugin.json
- name: Run demo in examples
run: |
./output/hrp run examples/demo-without-plugin/testcases/demo_without_funplugin.json

View File

@@ -1,6 +1,6 @@
# Release History # Release History
## 4.0.0 ## v4.0.0-alpha
- refactor: merge [hrp] into httprunner v4, which will include golang and python dual engine - refactor: merge [hrp] into httprunner v4, which will include golang and python dual engine

View File

@@ -1,135 +0,0 @@
{
"config": {
"name": "testcase description",
"variables": {},
"verify": false
},
"teststeps": [
{
"name": "/get",
"request": {
"url": "https://postman-echo.com/get",
"params": {
"foo1": "HDnY8",
"foo2": "34.5"
},
"method": "GET",
"headers": {
"Host": "postman-echo.com",
"User-Agent": "HttpRunnerPlus",
"Accept-Encoding": "gzip"
}
},
"validate": [
{
"eq": [
"status_code",
200
]
},
{
"eq": [
"headers.Content-Type",
"application/json; charset=utf-8"
]
},
{
"eq": [
"body.url",
"https://postman-echo.com/get?foo1=HDnY8&foo2=34.5"
]
}
]
},
{
"name": "/post",
"request": {
"url": "https://postman-echo.com/post",
"method": "POST",
"cookies": {
"sails.sid": "s%3Az_LpglkKxTvJ_eHVUH6V67drKp0AGWW-.PidabaXOnatLRP47hVyqqepl6BdrpEQzRlJQXtbIiwk"
},
"headers": {
"Host": "postman-echo.com",
"User-Agent": "Go-http-client/1.1",
"Content-Length": "28",
"Content-Type": "application/json; charset=UTF-8",
"Cookie": "sails.sid=s%3Az_LpglkKxTvJ_eHVUH6V67drKp0AGWW-.PidabaXOnatLRP47hVyqqepl6BdrpEQzRlJQXtbIiwk",
"Accept-Encoding": "gzip"
},
"json": {
"foo1": "HDnY8",
"foo2": 12.3
}
},
"validate": [
{
"eq": [
"status_code",
200
]
},
{
"eq": [
"headers.Content-Type",
"application/json; charset=utf-8"
]
},
{
"eq": [
"body.url",
"https://postman-echo.com/post"
]
}
]
},
{
"name": "/post",
"request": {
"url": "https://postman-echo.com/post",
"method": "POST",
"cookies": {
"sails.sid": "s%3AS5e7w0zQ0xAsCwh9L8T6R7QLYCO7_gtD.r8%2B2w9IWqEIfuVkrZjnxzm2xADIk34zKAWXRPapr%2FAw"
},
"headers": {
"Host": "postman-echo.com",
"User-Agent": "Go-http-client/1.1",
"Content-Length": "20",
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
"Cookie": "sails.sid=s%3AS5e7w0zQ0xAsCwh9L8T6R7QLYCO7_gtD.r8%2B2w9IWqEIfuVkrZjnxzm2xADIk34zKAWXRPapr%2FAw",
"Accept-Encoding": "gzip"
},
"data": {
"foo1": "HDnY8",
"foo2": "12.3"
}
},
"validate": [
{
"eq": [
"status_code",
200
]
},
{
"eq": [
"headers.Content-Type",
"application/json; charset=utf-8"
]
},
{
"eq": [
"body.data",
""
]
},
{
"eq": [
"body.url",
"https://postman-echo.com/post"
]
}
]
}
]
}

View File

@@ -1,81 +0,0 @@
config:
name: testcase description
variables: {}
verify: false
teststeps:
- name: /get
request:
headers:
Accept-Encoding: gzip
Host: postman-echo.com
User-Agent: HttpRunnerPlus
method: GET
params:
foo1: HDnY8
foo2: '34.5'
url: https://postman-echo.com/get
validate:
- eq:
- status_code
- 200
- eq:
- headers.Content-Type
- application/json; charset=utf-8
- eq:
- body.url
- https://postman-echo.com/get?foo1=HDnY8&foo2=34.5
- name: /post
request:
cookies:
sails.sid: s%3Az_LpglkKxTvJ_eHVUH6V67drKp0AGWW-.PidabaXOnatLRP47hVyqqepl6BdrpEQzRlJQXtbIiwk
headers:
Accept-Encoding: gzip
Content-Length: '28'
Content-Type: application/json; charset=UTF-8
Cookie: sails.sid=s%3Az_LpglkKxTvJ_eHVUH6V67drKp0AGWW-.PidabaXOnatLRP47hVyqqepl6BdrpEQzRlJQXtbIiwk
Host: postman-echo.com
User-Agent: Go-http-client/1.1
json:
foo1: HDnY8
foo2: 12.3
method: POST
url: https://postman-echo.com/post
validate:
- eq:
- status_code
- 200
- eq:
- headers.Content-Type
- application/json; charset=utf-8
- eq:
- body.url
- https://postman-echo.com/post
- name: /post
request:
cookies:
sails.sid: s%3AS5e7w0zQ0xAsCwh9L8T6R7QLYCO7_gtD.r8%2B2w9IWqEIfuVkrZjnxzm2xADIk34zKAWXRPapr%2FAw
data:
foo1: HDnY8
foo2: '12.3'
headers:
Accept-Encoding: gzip
Content-Length: '20'
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Cookie: sails.sid=s%3AS5e7w0zQ0xAsCwh9L8T6R7QLYCO7_gtD.r8%2B2w9IWqEIfuVkrZjnxzm2xADIk34zKAWXRPapr%2FAw
Host: postman-echo.com
User-Agent: Go-http-client/1.1
method: POST
url: https://postman-echo.com/post
validate:
- eq:
- status_code
- 200
- eq:
- headers.Content-Type
- application/json; charset=utf-8
- eq:
- body.data
- ''
- eq:
- body.url
- https://postman-echo.com/post

View File

@@ -1,47 +0,0 @@
config:
name: 'api test demo'
variables:
user_agent: iOS/10.3
device_sn: TESTCASE_SETUP_XXX
os_platform: ios
app_version: 2.8.6
base_url: 'https://postman-echo.com'
herader:
- Accept: '*/*'
Accept-Encoding: 'gzip, deflate, br'
Cache-Control: no-cache
Connection: keep-alive
Host: postman-echo.com
User-Agent: PostmanRuntime/7.28.4
verify: false
export:
- session_token
teststeps:
- name: 'test api /get'
api: api/get.json
variables:
user_agent: iOS/10.4
device_sn: $device_sn
os_platform: ios
app_version: 2.8.7
extract:
session_token: 'body.headers."postman-token"'
- name: 'test api /post'
api: api/post.json
variables:
user_agent: iOS/10.5
device_sn: $device_sn
os_platform: ios
app_version: 2.8.9
validate:
- { eq: [ status_code, 200 ] }
- { eq: [ body.headers.postman-token, ea19464c-ddd4-4724-abe9-5e2b254c2723 ] }
- name: 'test api /put'
api: api/put.json
variables:
user_agent: iOS/10.6
device_sn: $device_sn
os_platform: ios
app_version: 2.8.10
extract:
session_token: 'body.headers."postman-token"'

View File

@@ -1,18 +0,0 @@
{
"config": {
"name": "reference testcase test",
"base_url": "https://postman-echo.com",
"variables": {
"os_platform": "ios"
}
},
"teststeps": [
{
"name": "run demo_httprunner.json",
"testcase": "demo_httprunner.json",
"variables": {
"os_platform": "$os_platform"
}
}
]
}

View File

@@ -1,11 +0,0 @@
config:
name: "reference testcase test"
base_url: "https://postman-echo.com"
variables:
os_platform: 'ios'
teststeps:
- name: run demo_httprunner.yaml
testcase: demo_httprunner.yaml
variables:
os_platform: $os_platform

View File

@@ -1,40 +0,0 @@
config:
name: "think time test demo"
variables:
app_version: v1
user_agent: iOS/10.3
base_url: "https://postman-echo.com"
think_time:
strategy: random_percentage
setting:
min_percentage: 1.0
max_percentage: 1.5
limit: 4
verify: False
teststeps:
- name: get with params
request:
method: GET
url: /get
headers:
User-Agent: $user_agent,$app_version
validate:
- check: status_code
assert: equals
expect: 200
msg: check status code
- name: think time 1
think_time:
time: 3
- name: post with params
request:
method: POST
url: /post
headers:
User-Agent: $user_agent,$app_version
validate:
- check: status_code
assert: equals
expect: 200
msg: check status code

View File

@@ -2,9 +2,11 @@ package hrp
import ( import (
"fmt" "fmt"
"os"
"path/filepath" "path/filepath"
"strings" "strings"
"github.com/pkg/errors"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"github.com/httprunner/httprunner/hrp/internal/builtin" "github.com/httprunner/httprunner/hrp/internal/builtin"
@@ -92,9 +94,21 @@ func (tc *TCase) ToTestCase() (*TestCase, error) {
testCase := &TestCase{ testCase := &TestCase{
Config: tc.Config, Config: tc.Config,
} }
// locate project root dir by plugin path
projectRootDir, err := getProjectRootDirPath(testCase.Config.Path)
if err != nil {
return nil, errors.Wrap(err, "failed to get project root dir")
}
log.Info().Str("dir", projectRootDir).Msg("located project root dir")
for _, step := range tc.TestSteps { for _, step := range tc.TestSteps {
if step.APIPath != "" { if step.APIPath != "" {
path := filepath.Join(filepath.Dir(testCase.Config.Path), step.APIPath) path := filepath.Join(projectRootDir, step.APIPath)
if !builtin.IsFilePathExists(path) {
return nil, errors.New("referenced api file not found: " + path)
}
refAPI := APIPath(path) refAPI := APIPath(path)
step.APIContent = &refAPI step.APIContent = &refAPI
apiContent, err := step.APIContent.ToAPI() apiContent, err := step.APIContent.ToAPI()
@@ -106,7 +120,11 @@ func (tc *TCase) ToTestCase() (*TestCase, error) {
step: step, step: step,
}) })
} else if step.TestCasePath != "" { } else if step.TestCasePath != "" {
path := filepath.Join(filepath.Dir(testCase.Config.Path), step.TestCasePath) path := filepath.Join(projectRootDir, step.TestCasePath)
if !builtin.IsFilePathExists(path) {
return nil, errors.New("referenced testcase file not found: " + path)
}
refTestCase := TestCasePath(path) refTestCase := TestCasePath(path)
step.TestCaseContent = &refTestCase step.TestCaseContent = &refTestCase
tc, err := step.TestCaseContent.ToTestCase() tc, err := step.TestCaseContent.ToTestCase()
@@ -140,16 +158,29 @@ func (tc *TCase) ToTestCase() (*TestCase, error) {
return testCase, nil return testCase, nil
} }
func getProjectRootDirPath(path string) (rootDir string, err error) {
pluginPath, err := locatePlugin(path)
if err == nil {
rootDir = filepath.Dir(pluginPath)
return
}
// failed to locate project root dir
// maybe project plugin debugtalk.xx is not exist
// use current dir instead
return os.Getwd()
}
// APIPath implements IAPI interface. // APIPath implements IAPI interface.
type APIPath string type APIPath string
func (path *APIPath) ToString() string { func (path *APIPath) GetPath() string {
return fmt.Sprintf("%v", *path) return fmt.Sprintf("%v", *path)
} }
func (path *APIPath) ToAPI() (*API, error) { func (path *APIPath) ToAPI() (*API, error) {
api := &API{} api := &API{}
apiPath := path.ToString() apiPath := path.GetPath()
err := builtin.LoadFile(apiPath, api) err := builtin.LoadFile(apiPath, api)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -161,22 +192,23 @@ func (path *APIPath) ToAPI() (*API, error) {
// TestCasePath implements ITestCase interface. // TestCasePath implements ITestCase interface.
type TestCasePath string type TestCasePath string
func (path *TestCasePath) ToString() string { func (path *TestCasePath) GetPath() string {
return fmt.Sprintf("%v", *path) return fmt.Sprintf("%v", *path)
} }
func (path *TestCasePath) ToTestCase() (*TestCase, error) { func (path *TestCasePath) ToTestCase() (*TestCase, error) {
tc := &TCase{} tc := &TCase{}
casePath := path.ToString() casePath := path.GetPath()
err := builtin.LoadFile(casePath, tc) err := builtin.LoadFile(casePath, tc)
if err != nil { if err != nil {
return nil, err return nil, err
} }
err = convertCompatTestCase(tc) err = convertCompatTestCase(tc)
if err != nil { if err != nil {
return nil, err return nil, err
} }
tc.Config.Path = path.ToString() tc.Config.Path = casePath
testcase, err := tc.ToTestCase() testcase, err := tc.ToTestCase()
if err != nil { if err != nil {
return nil, err return nil, err

View File

@@ -8,20 +8,22 @@ import (
"github.com/httprunner/httprunner/hrp/internal/builtin" "github.com/httprunner/httprunner/hrp/internal/builtin"
) )
const templatesDir = "internal/scaffold/templates/" const (
templatesDir = "internal/scaffold/templates/"
hrpExamplesDir = "../examples/hrp"
)
var ( var (
demoTestCaseWithPluginJSONPath TestCasePath = templatesDir + "testcases/demo_with_funplugin.json" demoTestCaseWithPluginJSONPath TestCasePath = templatesDir + "testcases/demo_with_funplugin.json"
demoTestCaseWithPluginYAMLPath TestCasePath = templatesDir + "testcases/demo_with_funplugin.yaml" demoTestCaseWithPluginYAMLPath TestCasePath = templatesDir + "testcases/demo_with_funplugin.yaml"
demoTestCaseWithoutPluginJSONPath TestCasePath = templatesDir + "testcases/demo_without_funplugin.json" demoTestCaseWithoutPluginJSONPath TestCasePath = templatesDir + "testcases/demo_without_funplugin.json"
demoTestCaseWithoutPluginYAMLPath TestCasePath = templatesDir + "testcases/demo_without_funplugin.yaml" demoTestCaseWithoutPluginYAMLPath TestCasePath = templatesDir + "testcases/demo_without_funplugin.yaml"
demoTestCaseWithRefAPIPath TestCasePath = templatesDir + "testcases/demo_ref_api.json"
demoAPIGETPath APIPath = templatesDir + "/api/get.yml"
) )
var ( var (
demoRefAPIYAMLPath TestCasePath = "../examples/hrp/ref_api_test.yaml" demoTestCaseWithThinkTimePath TestCasePath = hrpExamplesDir + "/think_time_test.json"
demoRefTestCaseJSONPath TestCasePath = "../examples/hrp/ref_testcase_test.json"
demoThinkTimeJsonPath TestCasePath = "../examples/hrp/think_time_test.json"
demoAPIYAMLPath APIPath = "../examples/hrp/api/put.yml"
) )
var demoTestCaseWithPlugin = &TestCase{ var demoTestCaseWithPlugin = &TestCase{
@@ -152,41 +154,21 @@ var demoTestCaseWithoutPlugin = &TestCase{
func TestGenDemoTestCase(t *testing.T) { func TestGenDemoTestCase(t *testing.T) {
tCase, _ := demoTestCaseWithPlugin.ToTCase() tCase, _ := demoTestCaseWithPlugin.ToTCase()
err := builtin.Dump2JSON(tCase, demoTestCaseWithPluginJSONPath.ToString()) err := builtin.Dump2JSON(tCase, demoTestCaseWithPluginJSONPath.GetPath())
if err != nil { if err != nil {
t.Fail() t.Fail()
} }
err = builtin.Dump2YAML(tCase, demoTestCaseWithPluginYAMLPath.ToString()) err = builtin.Dump2YAML(tCase, demoTestCaseWithPluginYAMLPath.GetPath())
if err != nil { if err != nil {
t.Fail() t.Fail()
} }
tCase, _ = demoTestCaseWithoutPlugin.ToTCase() tCase, _ = demoTestCaseWithoutPlugin.ToTCase()
err = builtin.Dump2JSON(tCase, demoTestCaseWithoutPluginJSONPath.ToString()) err = builtin.Dump2JSON(tCase, demoTestCaseWithoutPluginJSONPath.GetPath())
if err != nil { if err != nil {
t.Fail() t.Fail()
} }
err = builtin.Dump2YAML(tCase, demoTestCaseWithoutPluginYAMLPath.ToString()) err = builtin.Dump2YAML(tCase, demoTestCaseWithoutPluginYAMLPath.GetPath())
if err != nil {
t.Fail()
}
}
func TestJsonDemoWithPlugin(t *testing.T) {
buildHashicorpGoPlugin()
defer removeHashicorpGoPlugin()
err := NewRunner(nil).Run(&demoTestCaseWithPluginJSONPath) // hrp.Run(testCase)
if err != nil {
t.Fail()
}
}
func TestYamlDemoWithPlugin(t *testing.T) {
buildHashicorpGoPlugin()
defer removeHashicorpGoPlugin()
err := NewRunner(nil).Run(&demoTestCaseWithPluginYAMLPath) // hrp.Run(testCase)
if err != nil { if err != nil {
t.Fail() t.Fail()
} }
@@ -195,11 +177,11 @@ func TestYamlDemoWithPlugin(t *testing.T) {
func TestLoadCase(t *testing.T) { func TestLoadCase(t *testing.T) {
tcJSON := &TCase{} tcJSON := &TCase{}
tcYAML := &TCase{} tcYAML := &TCase{}
err := builtin.LoadFile(demoTestCaseWithPluginJSONPath.ToString(), tcJSON) err := builtin.LoadFile(demoTestCaseWithPluginJSONPath.GetPath(), tcJSON)
if !assert.NoError(t, err) { if !assert.NoError(t, err) {
t.Fail() t.Fail()
} }
err = builtin.LoadFile(demoTestCaseWithPluginYAMLPath.ToString(), tcYAML) err = builtin.LoadFile(demoTestCaseWithPluginYAMLPath.GetPath(), tcYAML)
if !assert.NoError(t, err) { if !assert.NoError(t, err) {
t.Fail() t.Fail()
} }
@@ -218,7 +200,7 @@ func TestLoadCase(t *testing.T) {
} }
} }
func Test_convertCheckExpr(t *testing.T) { func TestConvertCheckExpr(t *testing.T) {
exprs := []struct { exprs := []struct {
before string before string
after string after string

View File

@@ -108,16 +108,16 @@ func CreateFile(filePath string, data string) error {
return nil return nil
} }
// isFilePathExists returns true if path exists, whether path is file or dir // IsPathExists returns true if path exists, whether path is file or dir
func isPathExists(path string) bool { func IsPathExists(path string) bool {
if _, err := os.Stat(path); os.IsNotExist(err) { if _, err := os.Stat(path); os.IsNotExist(err) {
return false return false
} }
return true return true
} }
// isFilePathExists returns true if path exists and path is file // IsFilePathExists returns true if path exists and path is file
func isFilePathExists(path string) bool { func IsFilePathExists(path string) bool {
info, err := os.Stat(path) info, err := os.Stat(path)
if err != nil { if err != nil {
// path not exists // path not exists
@@ -133,10 +133,10 @@ func isFilePathExists(path string) bool {
} }
func EnsureFolderExists(folderPath string) error { func EnsureFolderExists(folderPath string) error {
if !isPathExists(folderPath) { if !IsPathExists(folderPath) {
err := CreateFolder(folderPath) err := CreateFolder(folderPath)
return err return err
} else if isFilePathExists(folderPath) { } else if IsFilePathExists(folderPath) {
return fmt.Errorf("path %v should be directory", folderPath) return fmt.Errorf("path %v should be directory", folderPath)
} }
return nil return nil

View File

@@ -193,6 +193,11 @@ type API struct {
Extract map[string]string `json:"extract,omitempty" yaml:"extract,omitempty"` Extract map[string]string `json:"extract,omitempty" yaml:"extract,omitempty"`
Validators []interface{} `json:"validate,omitempty" yaml:"validate,omitempty"` Validators []interface{} `json:"validate,omitempty" yaml:"validate,omitempty"`
Export []string `json:"export,omitempty" yaml:"export,omitempty"` Export []string `json:"export,omitempty" yaml:"export,omitempty"`
Path string
}
func (api *API) GetPath() string {
return api.Path
} }
func (api *API) ToAPI() (*API, error) { func (api *API) ToAPI() (*API, error) {
@@ -210,6 +215,7 @@ type Validator struct {
// IAPI represents interface for api, // IAPI represents interface for api,
// includes API and APIPath. // includes API and APIPath.
type IAPI interface { type IAPI interface {
GetPath() string
ToAPI() (*API, error) ToAPI() (*API, error)
} }
@@ -219,8 +225,8 @@ type TStep struct {
Name string `json:"name" yaml:"name"` // required Name string `json:"name" yaml:"name"` // required
Request *Request `json:"request,omitempty" yaml:"request,omitempty"` Request *Request `json:"request,omitempty" yaml:"request,omitempty"`
APIPath string `json:"api,omitempty" yaml:"api,omitempty"` APIPath string `json:"api,omitempty" yaml:"api,omitempty"`
TestCasePath string `json:"testcase,omitempty" yaml:"testcase,omitempty"`
APIContent IAPI `json:"api_content,omitempty" yaml:"api_content,omitempty"` APIContent IAPI `json:"api_content,omitempty" yaml:"api_content,omitempty"`
TestCasePath string `json:"testcase,omitempty" yaml:"testcase,omitempty"`
TestCaseContent ITestCase `json:"testcase_content,omitempty" yaml:"testcase_content,omitempty"` TestCaseContent ITestCase `json:"testcase_content,omitempty" yaml:"testcase_content,omitempty"`
Transaction *Transaction `json:"transaction,omitempty" yaml:"transaction,omitempty"` Transaction *Transaction `json:"transaction,omitempty" yaml:"transaction,omitempty"`
Rendezvous *Rendezvous `json:"rendezvous,omitempty" yaml:"rendezvous,omitempty"` Rendezvous *Rendezvous `json:"rendezvous,omitempty" yaml:"rendezvous,omitempty"`
@@ -287,6 +293,10 @@ type TCase struct {
TestSteps []*TStep `json:"teststeps" yaml:"teststeps"` TestSteps []*TStep `json:"teststeps" yaml:"teststeps"`
} }
func (tc *TCase) Path() string {
return tc.Config.Path
}
// IStep represents interface for all types for teststeps, includes: // IStep represents interface for all types for teststeps, includes:
// StepRequest, StepRequestWithOptionalArgs, StepRequestValidation, StepRequestExtraction, // StepRequest, StepRequestWithOptionalArgs, StepRequestValidation, StepRequestExtraction,
// StepTestCaseWithOptionalArgs, // StepTestCaseWithOptionalArgs,
@@ -300,6 +310,7 @@ type IStep interface {
// ITestCase represents interface for testcases, // ITestCase represents interface for testcases,
// includes TestCase and TestCasePath. // includes TestCase and TestCasePath.
type ITestCase interface { type ITestCase interface {
GetPath() string
ToTestCase() (*TestCase, error) ToTestCase() (*TestCase, error)
ToTCase() (*TCase, error) ToTCase() (*TCase, error)
} }
@@ -311,6 +322,10 @@ type TestCase struct {
TestSteps []IStep TestSteps []IStep
} }
func (tc *TestCase) GetPath() string {
return tc.Config.Path
}
func (tc *TestCase) ToTestCase() (*TestCase, error) { func (tc *TestCase) ToTestCase() (*TestCase, error) {
return tc, nil return tc, nil
} }

View File

@@ -1,6 +1,7 @@
package hrp package hrp
import ( import (
"fmt"
"sort" "sort"
"testing" "testing"
"time" "time"
@@ -742,7 +743,7 @@ func TestParseParameters(t *testing.T) {
}{ }{
{ {
map[string]interface{}{ map[string]interface{}{
"username-password": "${parameterize(../examples/hrp/account.csv)}", "username-password": fmt.Sprintf("${parameterize(%s/account.csv)}", hrpExamplesDir),
"user_agent": []interface{}{"IOS/10.1", "IOS/10.2"}}, "user_agent": []interface{}{"IOS/10.1", "IOS/10.2"}},
6, 6,
}, },
@@ -782,17 +783,17 @@ func TestParseParametersError(t *testing.T) {
}{ }{
{ {
map[string]interface{}{ map[string]interface{}{
"username_password": "${parameterize(../examples/hrp/account.csv)}", "username_password": fmt.Sprintf("${parameterize(%s/account.csv)}", hrpExamplesDir),
"user_agent": []interface{}{"IOS/10.1", "IOS/10.2"}}, "user_agent": []interface{}{"IOS/10.1", "IOS/10.2"}},
}, },
{ {
map[string]interface{}{ map[string]interface{}{
"username-password": "${parameterize(../examples/hrp/account.csv)}", "username-password": fmt.Sprintf("${parameterize(%s/account.csv)}", hrpExamplesDir),
"user-agent": []interface{}{"IOS/10.1", "IOS/10.2"}}, "user-agent": []interface{}{"IOS/10.1", "IOS/10.2"}},
}, },
{ {
map[string]interface{}{ map[string]interface{}{
"username-password": "${param(../examples/hrp/account.csv)}", "username-password": fmt.Sprintf("${param(%s/account.csv)}", hrpExamplesDir),
"user_agent": []interface{}{"IOS/10.1", "IOS/10.2"}}, "user_agent": []interface{}{"IOS/10.1", "IOS/10.2"}},
}, },
} }

View File

@@ -40,14 +40,14 @@ func removeHashicorpPyPlugin() {
os.Remove(templatesDir + "debugtalk.py") os.Remove(templatesDir + "debugtalk.py")
} }
func TestHttpRunnerWithGoPlugin(t *testing.T) { func TestRunCaseWithGoPlugin(t *testing.T) {
buildHashicorpGoPlugin() buildHashicorpGoPlugin()
defer removeHashicorpGoPlugin() defer removeHashicorpGoPlugin()
assertRunTestCases(t) assertRunTestCases(t)
} }
func TestHttpRunnerWithPythonPlugin(t *testing.T) { func TestRunCaseWithPythonPlugin(t *testing.T) {
buildHashicorpPyPlugin() buildHashicorpPyPlugin()
defer removeHashicorpPyPlugin() defer removeHashicorpPyPlugin()
@@ -59,19 +59,19 @@ func assertRunTestCases(t *testing.T) {
Config: NewConfig("TestCase1"). Config: NewConfig("TestCase1").
SetBaseURL("http://httpbin.org"), SetBaseURL("http://httpbin.org"),
TestSteps: []IStep{ TestSteps: []IStep{
NewStep("headers"). 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", "check http response Content-Type"), AssertEqual("headers.\"Content-Type\"", "application/json", "check http response Content-Type"),
NewStep("user-agent"). NewStep("testcase1-step2").
GET("/user-agent"). GET("/user-agent").
Validate(). Validate().
AssertEqual("status_code", 200, "check status code"). AssertEqual("status_code", 200, "check status code").
AssertEqual("headers.\"Content-Type\"", "application/json", "check http response Content-Type"), AssertEqual("headers.\"Content-Type\"", "application/json", "check http response Content-Type"),
NewStep("TestCase3").CallRefCase( NewStep("testcase1-step3").CallRefCase(
&TestCase{ &TestCase{
Config: NewConfig("TestCase3").SetBaseURL("http://httpbin.org"), Config: NewConfig("testcase1-step3-ref-case").SetBaseURL("http://httpbin.org"),
TestSteps: []IStep{ TestSteps: []IStep{
NewStep("ip"). NewStep("ip").
GET("/ip"). GET("/ip").
@@ -81,32 +81,23 @@ func assertRunTestCases(t *testing.T) {
}, },
}, },
), ),
NewStep("TestCase4").CallRefCase(&demoRefAPIYAMLPath), NewStep("testcase1-step4").CallRefCase(&demoTestCaseWithPluginJSONPath),
NewStep("TestCase5").CallRefCase(&demoTestCaseWithPluginJSONPath),
}, },
} }
testcase2 := &TestCase{ testcase2 := &TestCase{
Config: NewConfig("TestCase2").SetWeight(3), Config: NewConfig("TestCase2").SetWeight(3),
} }
testcase3 := &TestCase{
Config: NewConfig("TestCase1").
SetBaseURL("https://postman-echo.com"),
TestSteps: []IStep{
NewStep("TestCase5").CallRefAPI(&demoAPIYAMLPath),
},
}
testcase4 := &demoRefTestCaseJSONPath
r := NewRunner(t) r := NewRunner(t)
r.SetPluginLogOn() r.SetPluginLogOn()
err := r.Run(testcase1, testcase2, testcase3, testcase4) err := r.Run(testcase1, testcase2)
if err != nil { if err != nil {
t.Fatalf("run testcase error: %v", err) t.Fatalf("run testcase error: %v", err)
} }
} }
func TestInitRendezvous(t *testing.T) { func TestRunCaseWithRendezvous(t *testing.T) {
rendezvousBonudaryTestcase := &TestCase{ rendezvousBoundaryTestcase := &TestCase{
Config: NewConfig("run request with functions"). Config: NewConfig("run request with functions").
SetBaseURL("https://postman-echo.com"). SetBaseURL("https://postman-echo.com").
WithVariables(map[string]interface{}{ WithVariables(map[string]interface{}{
@@ -155,7 +146,7 @@ func TestInitRendezvous(t *testing.T) {
{number: 100, percent: 1, timeout: 5000}, {number: 100, percent: 1, timeout: 5000},
} }
rendezvousList := initRendezvous(rendezvousBonudaryTestcase, 100) rendezvousList := 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 {
@@ -170,7 +161,7 @@ func TestInitRendezvous(t *testing.T) {
} }
} }
func TestThinkTime(t *testing.T) { func TestRunCaseWithThinkTime(t *testing.T) {
buildHashicorpGoPlugin() buildHashicorpGoPlugin()
defer removeHashicorpGoPlugin() defer removeHashicorpGoPlugin()
@@ -205,7 +196,8 @@ func TestThinkTime(t *testing.T) {
{ {
Config: NewConfig("TestCase5"), Config: NewConfig("TestCase5"),
TestSteps: []IStep{ TestSteps: []IStep{
NewStep("thinkTime").CallRefCase(&demoThinkTimeJsonPath), // 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),
}, },
}, },
} }
@@ -247,3 +239,47 @@ func TestGenHTMLReport(t *testing.T) {
t.Error(err) t.Error(err)
} }
} }
func TestRunCaseWithPluginJSON(t *testing.T) {
buildHashicorpGoPlugin()
defer removeHashicorpGoPlugin()
err := NewRunner(nil).Run(&demoTestCaseWithPluginJSONPath) // hrp.Run(testCase)
if err != nil {
t.Fail()
}
}
func TestRunCaseWithPluginYAML(t *testing.T) {
buildHashicorpGoPlugin()
defer removeHashicorpGoPlugin()
err := NewRunner(nil).Run(&demoTestCaseWithPluginYAMLPath) // hrp.Run(testCase)
if err != nil {
t.Fail()
}
}
func TestRunCaseWithRefAPI(t *testing.T) {
buildHashicorpGoPlugin()
defer removeHashicorpGoPlugin()
err := NewRunner(nil).Run(&demoTestCaseWithRefAPIPath)
if err != nil {
t.Fail()
}
testcase := &TestCase{
Config: NewConfig("TestCase").
SetBaseURL("https://postman-echo.com"),
TestSteps: []IStep{
NewStep("run referenced api").CallRefAPI(&demoAPIGETPath),
},
}
r := NewRunner(t)
err = r.Run(testcase)
if err != nil {
t.Fail()
}
}

View File

@@ -1,6 +1,11 @@
package hrp package hrp
import "fmt" import (
"fmt"
"os"
"github.com/rs/zerolog/log"
)
// NewConfig returns a new constructed testcase config with specified testcase name. // NewConfig returns a new constructed testcase config with specified testcase name.
func NewConfig(name string) *TConfig { func NewConfig(name string) *TConfig {
@@ -163,7 +168,12 @@ func (s *StepRequest) PATCH(url string) *StepRequestWithOptionalArgs {
// CallRefCase calls a referenced testcase. // CallRefCase calls a referenced testcase.
func (s *StepRequest) CallRefCase(tc ITestCase) *StepTestCaseWithOptionalArgs { func (s *StepRequest) CallRefCase(tc ITestCase) *StepTestCaseWithOptionalArgs {
s.step.TestCaseContent, _ = tc.ToTestCase() var err error
s.step.TestCaseContent, err = tc.ToTestCase()
if err != nil {
log.Error().Err(err).Msg("failed to load testcase")
os.Exit(1)
}
return &StepTestCaseWithOptionalArgs{ return &StepTestCaseWithOptionalArgs{
step: s.step, step: s.step,
} }
@@ -171,7 +181,12 @@ func (s *StepRequest) CallRefCase(tc ITestCase) *StepTestCaseWithOptionalArgs {
// CallRefAPI calls a referenced api. // CallRefAPI calls a referenced api.
func (s *StepRequest) CallRefAPI(api IAPI) *StepAPIWithOptionalArgs { func (s *StepRequest) CallRefAPI(api IAPI) *StepAPIWithOptionalArgs {
s.step.APIContent, _ = api.ToAPI() var err error
s.step.APIContent, err = api.ToAPI()
if err != nil {
log.Error().Err(err).Msg("failed to load api")
os.Exit(1)
}
return &StepAPIWithOptionalArgs{ return &StepAPIWithOptionalArgs{
step: s.step, step: s.step,
} }

View File

@@ -1,25 +0,0 @@
package tests
import (
"testing"
"github.com/httprunner/httprunner/hrp"
)
// generated by examples/data/har/demo.har using HttpRunner v3.1.6
var (
demoHttpRunnerJSONPath hrp.TestCasePath = "../../examples/hrp/demo_httprunner.json"
demoHttpRunnerYAMLPath hrp.TestCasePath = "../../examples/hrp/demo_httprunner.yaml"
)
func TestCompatTestCase(t *testing.T) {
err := hrp.NewRunner(t).Run(&demoHttpRunnerJSONPath)
if err != nil {
t.Fatalf("run testcase error: %v", err)
}
err = hrp.NewRunner(t).Run(&demoHttpRunnerYAMLPath)
if err != nil {
t.Fatalf("run testcase error: %v", err)
}
}