mirror of
https://github.com/httprunner/httprunner.git
synced 2026-06-01 22:09:35 +08:00
new feature: testcases can be layered, now we can define interface in api block individually
This commit is contained in:
@@ -39,7 +39,7 @@ To ensure the installation or upgrade is successful, you can execute command `at
|
|||||||
|
|
||||||
```text
|
```text
|
||||||
$ ate -V
|
$ ate -V
|
||||||
ApiTestEngine version: 0.6.0
|
ApiTestEngine version: 0.7.0
|
||||||
```
|
```
|
||||||
|
|
||||||
Execute the command `ate -h` to view command help.
|
Execute the command `ate -h` to view command help.
|
||||||
@@ -75,7 +75,7 @@ To install mail helper, run this command in your terminal:
|
|||||||
$ pip install -U git+https://github.com/debugtalk/jenkins-mail-py.git#egg=jenkins-mail-py
|
$ pip install -U git+https://github.com/debugtalk/jenkins-mail-py.git#egg=jenkins-mail-py
|
||||||
$ ate -V
|
$ ate -V
|
||||||
jenkins-mail-py version: 0.2.5
|
jenkins-mail-py version: 0.2.5
|
||||||
ApiTestEngine version: 0.6.0
|
ApiTestEngine version: 0.7.0
|
||||||
```
|
```
|
||||||
|
|
||||||
With [`jenkins-mail-py`][jenkins-mail-py] installed, you can see more optional arguments.
|
With [`jenkins-mail-py`][jenkins-mail-py] installed, you can see more optional arguments.
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
__version__ = '0.6.3'
|
__version__ = '0.7.0'
|
||||||
@@ -24,3 +24,6 @@ class FunctionNotFound(NameError):
|
|||||||
|
|
||||||
class VariableNotFound(NameError):
|
class VariableNotFound(NameError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
class ApiNotFound(NameError):
|
||||||
|
pass
|
||||||
|
|||||||
69
ate/utils.py
69
ate/utils.py
@@ -11,7 +11,7 @@ import string
|
|||||||
import types
|
import types
|
||||||
|
|
||||||
import yaml
|
import yaml
|
||||||
from ate import exception
|
from ate import exception, testcase
|
||||||
from requests.structures import CaseInsensitiveDict
|
from requests.structures import CaseInsensitiveDict
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -24,6 +24,7 @@ except NameError:
|
|||||||
PYTHON_VERSION = 3
|
PYTHON_VERSION = 3
|
||||||
|
|
||||||
SECRET_KEY = "DebugTalk"
|
SECRET_KEY = "DebugTalk"
|
||||||
|
api_overall_dict = {}
|
||||||
|
|
||||||
def gen_random_string(str_len):
|
def gen_random_string(str_len):
|
||||||
return ''.join(
|
return ''.join(
|
||||||
@@ -125,6 +126,7 @@ def load_testcases_by_path(path):
|
|||||||
"testcases": []
|
"testcases": []
|
||||||
}
|
}
|
||||||
testcases_list = load_testcases(path)
|
testcases_list = load_testcases(path)
|
||||||
|
dir_path = os.path.dirname(os.path.abspath(path))
|
||||||
|
|
||||||
for item in testcases_list:
|
for item in testcases_list:
|
||||||
for key in item:
|
for key in item:
|
||||||
@@ -132,13 +134,76 @@ def load_testcases_by_path(path):
|
|||||||
testset["config"].update(item["config"])
|
testset["config"].update(item["config"])
|
||||||
testset["name"] = item["config"].get("name", "")
|
testset["name"] = item["config"].get("name", "")
|
||||||
elif key == "test":
|
elif key == "test":
|
||||||
testset["testcases"].append(item["test"])
|
test_dict = item["test"]
|
||||||
|
if "api" in test_dict:
|
||||||
|
update_test_info(test_dict, dir_path)
|
||||||
|
|
||||||
|
testset["testcases"].append(test_dict)
|
||||||
|
|
||||||
return [testset] if testset["testcases"] else []
|
return [testset] if testset["testcases"] else []
|
||||||
|
|
||||||
else:
|
else:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
def update_test_info(test_dict, dir_path):
|
||||||
|
api_call = test_dict["api"]
|
||||||
|
function_meta = testcase.parse_function(api_call)
|
||||||
|
func_name = function_meta["func_name"]
|
||||||
|
api_info = get_api_definition(func_name, dir_path)
|
||||||
|
test_dict.update(api_info)
|
||||||
|
|
||||||
|
def get_api_definition(name, dir_path):
|
||||||
|
""" get expected api from dir_path upward recursively
|
||||||
|
@param
|
||||||
|
name: api name
|
||||||
|
dir_path: start search dir path
|
||||||
|
@return
|
||||||
|
expected api info if found, otherwise raise ApiNotFound exception
|
||||||
|
"""
|
||||||
|
api_dir_dict = api_overall_dict.get(dir_path)
|
||||||
|
if not api_dir_dict:
|
||||||
|
api_dir_dict = load_api_definition(dir_path)
|
||||||
|
api_overall_dict[dir_path] = api_dir_dict
|
||||||
|
|
||||||
|
api_info = api_dir_dict.get(name)
|
||||||
|
if api_info:
|
||||||
|
return api_info
|
||||||
|
|
||||||
|
parent_dir_path = os.path.dirname(dir_path)
|
||||||
|
if dir_path == parent_dir_path:
|
||||||
|
# system root path
|
||||||
|
err_msg = "{} not found in recursive upward path!".format(name)
|
||||||
|
raise exception.ApiNotFound(err_msg)
|
||||||
|
|
||||||
|
return get_api_definition(name, parent_dir_path)
|
||||||
|
|
||||||
|
def load_api_definition(dir_path):
|
||||||
|
""" load all api definitions in specified dir path
|
||||||
|
@param (str) dir_path
|
||||||
|
@return (dict) all api definitions in dir_path merged in one dict
|
||||||
|
"""
|
||||||
|
api_files = load_folder_files(dir_path, file_type="api", recursive=False)
|
||||||
|
|
||||||
|
api_def_list = []
|
||||||
|
for api_file in api_files:
|
||||||
|
api_def_list.extend(load_testcases(api_file))
|
||||||
|
|
||||||
|
api_dir_dict = {}
|
||||||
|
|
||||||
|
for item in api_def_list:
|
||||||
|
for key in item:
|
||||||
|
if key == "api":
|
||||||
|
api_def = item["api"].pop("def")
|
||||||
|
function_meta = testcase.parse_function(api_def)
|
||||||
|
func_name = function_meta["func_name"]
|
||||||
|
|
||||||
|
api_info = {}
|
||||||
|
api_info["function_meta"] = function_meta
|
||||||
|
api_info.update(item["api"])
|
||||||
|
api_dir_dict[func_name] = api_info
|
||||||
|
|
||||||
|
return api_dir_dict
|
||||||
|
|
||||||
def query_json(json_content, query, delimiter='.'):
|
def query_json(json_content, query, delimiter='.'):
|
||||||
""" Do an xpath-like query with json_content.
|
""" Do an xpath-like query with json_content.
|
||||||
@param (json_content) json_content
|
@param (json_content) json_content
|
||||||
|
|||||||
26
tests/data/api.yml
Normal file
26
tests/data/api.yml
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
- api:
|
||||||
|
def: get_token($user_name, $device_sn, $os_platform, $app_version)
|
||||||
|
request:
|
||||||
|
url: /api/get-token
|
||||||
|
method: POST
|
||||||
|
headers:
|
||||||
|
user_agent: $user_agent
|
||||||
|
device_sn: $device_sn
|
||||||
|
os_platform: $os_platform
|
||||||
|
app_version: $app_version
|
||||||
|
json:
|
||||||
|
sign: ${get_sign($user_agent, $device_sn, $os_platform, $app_version)}
|
||||||
|
validators:
|
||||||
|
- {"check": "status_code", "comparator": "eq", "expected": 200}
|
||||||
|
- {"check": "content.token", "comparator": "len_eq", "expected": 16}
|
||||||
|
|
||||||
|
- api:
|
||||||
|
def: create_user($uid, $user_name, $user_password, $token)
|
||||||
|
request:
|
||||||
|
url: /api/users/$uid
|
||||||
|
method: POST
|
||||||
|
headers:
|
||||||
|
token: $token
|
||||||
|
json:
|
||||||
|
name: $user_name
|
||||||
|
password: $user_password
|
||||||
40
tests/data/demo_testset_layer.yml
Normal file
40
tests/data/demo_testset_layer.yml
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
- config:
|
||||||
|
name: "create user testsets."
|
||||||
|
variable_binds:
|
||||||
|
- user_agent: 'iOS/10.3'
|
||||||
|
- device_sn: ${gen_random_string(15)}
|
||||||
|
- os_platform: 'ios'
|
||||||
|
- app_version: '2.8.6'
|
||||||
|
request:
|
||||||
|
base_url: $BASE_URL
|
||||||
|
headers:
|
||||||
|
Content-Type: application/json
|
||||||
|
device_sn: $device_sn
|
||||||
|
|
||||||
|
- test:
|
||||||
|
name: get token
|
||||||
|
api: get_token($user_agent, $device_sn, $os_platform, $app_version)
|
||||||
|
extract_binds:
|
||||||
|
- token: content.token
|
||||||
|
|
||||||
|
- test:
|
||||||
|
name: create user which does not exist
|
||||||
|
variable_binds:
|
||||||
|
- uid: 1000
|
||||||
|
- user_name: "user1"
|
||||||
|
- user_password: "123456"
|
||||||
|
api: create_user($uid, $user_name, $user_password, $token)
|
||||||
|
validators:
|
||||||
|
- {"check": "status_code", "comparator": "eq", "expected": 201}
|
||||||
|
- {"check": "content.success", "comparator": "eq", "expected": true}
|
||||||
|
|
||||||
|
- test:
|
||||||
|
name: create user which does not exist
|
||||||
|
variable_binds:
|
||||||
|
- uid: 1000
|
||||||
|
- user_name: "user1"
|
||||||
|
- user_password: "123456"
|
||||||
|
api: create_user($uid, $user_name, $user_password, $token)
|
||||||
|
validators:
|
||||||
|
- {"check": "status_code", "comparator": "eq", "expected": 500}
|
||||||
|
- {"check": "content.success", "comparator": "eq", "expected": false}
|
||||||
@@ -107,3 +107,11 @@ class TestRunner(ApiServerUnittest):
|
|||||||
results = self.test_runner.run_testsets(testsets)
|
results = self.test_runner.run_testsets(testsets)
|
||||||
self.assertEqual(len(results), 1)
|
self.assertEqual(len(results), 1)
|
||||||
self.assertEqual(results, [[True] * 3])
|
self.assertEqual(results, [[True] * 3])
|
||||||
|
|
||||||
|
def test_run_testset_layered(self):
|
||||||
|
testcase_file_path = os.path.join(
|
||||||
|
os.getcwd(), 'tests/data/demo_testset_layer.yml')
|
||||||
|
testsets = utils.load_testcases_by_path(testcase_file_path)
|
||||||
|
results = self.test_runner.run_testsets(testsets)
|
||||||
|
self.assertEqual(len(results), 1)
|
||||||
|
self.assertEqual(results, [[True] * 3])
|
||||||
|
|||||||
@@ -44,7 +44,8 @@ class TestUtils(ApiServerUnittest):
|
|||||||
self.assertNotIn(file1, files)
|
self.assertNotIn(file1, files)
|
||||||
|
|
||||||
files = utils.load_folder_files(folder, file_type="api", recursive=True)
|
files = utils.load_folder_files(folder, file_type="api", recursive=True)
|
||||||
self.assertEqual(files, [])
|
api_file = os.path.join(os.getcwd(), 'tests', 'data', 'api.yml')
|
||||||
|
self.assertEqual(files[0], api_file)
|
||||||
|
|
||||||
def test_load_testcases_by_path_files(self):
|
def test_load_testcases_by_path_files(self):
|
||||||
testsets_list = []
|
testsets_list = []
|
||||||
@@ -125,6 +126,36 @@ class TestUtils(ApiServerUnittest):
|
|||||||
testset_list_3 = utils.load_testcases_by_path(path)
|
testset_list_3 = utils.load_testcases_by_path(path)
|
||||||
self.assertEqual(testset_list_3, [])
|
self.assertEqual(testset_list_3, [])
|
||||||
|
|
||||||
|
def test_load_testcases_by_path_layered(self):
|
||||||
|
path = os.path.join(
|
||||||
|
os.getcwd(), 'tests/data/demo_testset_layer.yml')
|
||||||
|
testsets_list = utils.load_testcases_by_path(path)
|
||||||
|
self.assertIn("variable_binds", testsets_list[0]["config"])
|
||||||
|
self.assertIn("request", testsets_list[0]["config"])
|
||||||
|
print(testsets_list[0]["testcases"][0])
|
||||||
|
self.assertIn("request", testsets_list[0]["testcases"][0])
|
||||||
|
self.assertIn("url", testsets_list[0]["testcases"][0]["request"])
|
||||||
|
self.assertIn("validators", testsets_list[0]["testcases"][0])
|
||||||
|
|
||||||
|
def test_load_api_definition(self):
|
||||||
|
path = os.path.join(
|
||||||
|
os.getcwd(), 'tests/data')
|
||||||
|
api_dir_dict = utils.load_api_definition(path)
|
||||||
|
self.assertIn("get_token", api_dir_dict)
|
||||||
|
self.assertEqual("/api/get-token", api_dir_dict["get_token"]["request"]["url"])
|
||||||
|
self.assertIn("$user_name", api_dir_dict["get_token"]["function_meta"]["args"])
|
||||||
|
self.assertIn("create_user", api_dir_dict)
|
||||||
|
|
||||||
|
def test_get_api_definition(self):
|
||||||
|
path = os.path.join(
|
||||||
|
os.getcwd(), 'tests/data')
|
||||||
|
api_info = utils.get_api_definition("get_token", path)
|
||||||
|
self.assertEqual("/api/get-token", api_info["request"]["url"])
|
||||||
|
self.assertIn(path, utils.api_overall_dict)
|
||||||
|
|
||||||
|
with self.assertRaises(exception.ApiNotFound):
|
||||||
|
utils.get_api_definition("api_not_exist", path)
|
||||||
|
|
||||||
def test_query_json(self):
|
def test_query_json(self):
|
||||||
json_content = {
|
json_content = {
|
||||||
"ids": [1, 2, 3, 4],
|
"ids": [1, 2, 3, 4],
|
||||||
|
|||||||
Reference in New Issue
Block a user