refactor testcase layer

This commit is contained in:
httprunner
2018-05-02 16:29:35 +08:00
parent d8ae4c0660
commit 7e263a0cbc
20 changed files with 505 additions and 937 deletions

View File

@@ -1,98 +0,0 @@
# Hello World
This example shows you how to organize and run testcases in layer.
## file structure
According to rules, all testcase definition files should be placed in `tests` folder, and testing reports will be generated in `reports` folder.
```text
$ cd httprunner/examples/HelloWorld
$ tree .
.
├── README.md
├── reports
│ └── smoketest
│ └── 2018-02-09-16-25-54.html
└── tests
├── __init__.py
├── api
│ └── basic.yml
├── debugtalk.py
├── suite
│ ├── create_and_check.yml
│ └── setup.yml
└── testcases
└── smoketest.yml
```
## Start server
In order to run test, we need a backend service, and here we will use `api_server` located in our unittests.
```bash
$ cd httprunner
$ export FLASK_APP=tests/api_server.py
$ flask run
* Serving Flask app "tests.api_server"
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
```
## run testcases
When you want to run testcases, you should make sure you are in the root directory of your project. In this example, that is the HelloWorld folder path.
```bash
$ cd httprunner/examples/HelloWorld
```
Then, run the testcase with `hrun` command.
```
$ hrun tests/testcases/smoketest.yml
Running tests...
----------------------------------------------------------------------
get token ... INFO:root: Start to POST http://127.0.0.1:5000/api/get-token
INFO:root: status_code: 200, response_time: 12 ms, response_length: 46 bytes
OK (0.018492)s
reset all users ... INFO:root: Start to GET http://127.0.0.1:5000/api/reset-all
INFO:root: status_code: 200, response_time: 5 ms, response_length: 17 bytes
OK (0.006153)s
make sure user 1000 does not exist ... INFO:root: Start to GET http://127.0.0.1:5000/api/users/1000
ERROR:root: Failed to GET http://127.0.0.1:5000/api/users/1000! exception msg: 404 Client Error: NOT FOUND for url: http://127.0.0.1:5000/api/users/1000
OK (0.010638)s
create user 1000 ... INFO:root: Start to POST http://127.0.0.1:5000/api/users/1000
INFO:root: status_code: 201, response_time: 9 ms, response_length: 54 bytes
OK (0.010303)s
check if user 1000 exists ... INFO:root: Start to GET http://127.0.0.1:5000/api/users/1000
INFO:root: status_code: 200, response_time: 11 ms, response_length: 66 bytes
OK (0.013168)s
make sure user 1001 does not exist ... INFO:root: Start to GET http://127.0.0.1:5000/api/users/1001
ERROR:root: Failed to GET http://127.0.0.1:5000/api/users/1001! exception msg: 404 Client Error: NOT FOUND for url: http://127.0.0.1:5000/api/users/1001
OK (0.013631)s
create user 1001 ... INFO:root: Start to POST http://127.0.0.1:5000/api/users/1001
INFO:root: status_code: 201, response_time: 6 ms, response_length: 54 bytes
OK (0.007490)s
check if user 1001 exists ... INFO:root: Start to GET http://127.0.0.1:5000/api/users/1001
INFO:root: status_code: 200, response_time: 9 ms, response_length: 66 bytes
OK (0.011435)s
----------------------------------------------------------------------
Ran 8 tests in 0.092s
OK
Generating HTML reports...
Template is not specified, load default template instead.
Reports generated: /Users/debugtalk/MyProjects/HttpRunner-dev/HttpRunner/examples/HelloWorld/reports/smoketest/2018-02-09-16-38-14.html
```
After the running is over, you will get a testing report, which is in HTML format.
```bash
$ open reports/smoketest/2018-02-09-16-38-14.html
```
![](test-report.jpg)

View File

@@ -1,205 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>Test Result</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7" crossorigin="anonymous">
</head>
<body>
<div class="container">
<div class="row">
<div class="col-xs-12">
<h2 class="text-capitalize">Test Result</h2>
<p class='attribute'><strong>Start Time: </strong>2018-02-09 16:25:53</p>
<p class='attribute'><strong>Duration: </strong>0.141s</p>
<p class='attribute'><strong>Status: </strong>Pass: 11</p>
</div>
</div>
<div class="row">
<div class="col-xs-12 col-sm-10 col-md-10">
<table class='table table-hover table-responsive'>
<thead>
<tr>
<th>smoketest</th>
<th>Status</th>
</tr>
</thead>
<tbody>
<tr class='success'>
<td class="col-xs-9">get token</td>
<td class="col-xs-3">
<span class="label label-success">
Pass
</span>
</td>
</tr>
<tr class='success'>
<td class="col-xs-9">reset all users</td>
<td class="col-xs-3">
<span class="label label-success">
Pass
</span>
</td>
</tr>
<tr class='success'>
<td class="col-xs-9">make sure user 1000 does not exist</td>
<td class="col-xs-3">
<span class="label label-success">
Pass
</span>
</td>
</tr>
<tr class='success'>
<td class="col-xs-9">create user 1000</td>
<td class="col-xs-3">
<span class="label label-success">
Pass
</span>
</td>
</tr>
<tr class='success'>
<td class="col-xs-9">check if user 1000 exists</td>
<td class="col-xs-3">
<span class="label label-success">
Pass
</span>
</td>
</tr>
<tr class='success'>
<td class="col-xs-9">make sure user 1001 does not exist</td>
<td class="col-xs-3">
<span class="label label-success">
Pass
</span>
</td>
</tr>
<tr class='success'>
<td class="col-xs-9">create user 1001</td>
<td class="col-xs-3">
<span class="label label-success">
Pass
</span>
</td>
</tr>
<tr class='success'>
<td class="col-xs-9">check if user 1001 exists</td>
<td class="col-xs-3">
<span class="label label-success">
Pass
</span>
</td>
</tr>
<tr class='success'>
<td class="col-xs-9">make sure user 1002 does not exist</td>
<td class="col-xs-3">
<span class="label label-success">
Pass
</span>
</td>
</tr>
<tr class='success'>
<td class="col-xs-9">create user 1002</td>
<td class="col-xs-3">
<span class="label label-success">
Pass
</span>
</td>
</tr>
<tr class='success'>
<td class="col-xs-9">check if user 1002 exists</td>
<td class="col-xs-3">
<span class="label label-success">
Pass
</span>
</td>
</tr>
<tr>
<td>
Total Test Runned: 11
</td>
<td>
<span>Pass: 11</span>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<script src="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-2.2.4.min.js"></script>
<script type="text/javascript">
$(document).ready(function(){
$('td').on('click', '.btn', function(e){
e.preventDefault();
var $this = $(this);
var $nextRow = $this.closest('tr').next('tr');
$nextRow.slideToggle("fast");
$this.text(function(i, text){
if (text === 'View') {
return 'Hide';
} else {
return 'View';
};
});
});
});
</script>
</body>
</html>

View File

@@ -1,166 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>Test Result</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7" crossorigin="anonymous">
</head>
<body>
<div class="container">
<div class="row">
<div class="col-xs-12">
<h2 class="text-capitalize">Test Result</h2>
<p class='attribute'><strong>Start Time: </strong>2018-02-09 16:38:14</p>
<p class='attribute'><strong>Duration: </strong>0.092s</p>
<p class='attribute'><strong>Status: </strong>Pass: 8</p>
</div>
</div>
<div class="row">
<div class="col-xs-12 col-sm-10 col-md-10">
<table class='table table-hover table-responsive'>
<thead>
<tr>
<th>smoketest</th>
<th>Status</th>
</tr>
</thead>
<tbody>
<tr class='success'>
<td class="col-xs-9">get token</td>
<td class="col-xs-3">
<span class="label label-success">
Pass
</span>
</td>
</tr>
<tr class='success'>
<td class="col-xs-9">reset all users</td>
<td class="col-xs-3">
<span class="label label-success">
Pass
</span>
</td>
</tr>
<tr class='success'>
<td class="col-xs-9">make sure user 1000 does not exist</td>
<td class="col-xs-3">
<span class="label label-success">
Pass
</span>
</td>
</tr>
<tr class='success'>
<td class="col-xs-9">create user 1000</td>
<td class="col-xs-3">
<span class="label label-success">
Pass
</span>
</td>
</tr>
<tr class='success'>
<td class="col-xs-9">check if user 1000 exists</td>
<td class="col-xs-3">
<span class="label label-success">
Pass
</span>
</td>
</tr>
<tr class='success'>
<td class="col-xs-9">make sure user 1001 does not exist</td>
<td class="col-xs-3">
<span class="label label-success">
Pass
</span>
</td>
</tr>
<tr class='success'>
<td class="col-xs-9">create user 1001</td>
<td class="col-xs-3">
<span class="label label-success">
Pass
</span>
</td>
</tr>
<tr class='success'>
<td class="col-xs-9">check if user 1001 exists</td>
<td class="col-xs-3">
<span class="label label-success">
Pass
</span>
</td>
</tr>
<tr>
<td>
Total Test Runned: 8
</td>
<td>
<span>Pass: 8</span>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<script src="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-2.2.4.min.js"></script>
<script type="text/javascript">
$(document).ready(function(){
$('td').on('click', '.btn', function(e){
e.preventDefault();
var $this = $(this);
var $nextRow = $this.closest('tr').next('tr');
$nextRow.slideToggle("fast");
$this.text(function(i, text){
if (text === 'View') {
return 'Hide';
} else {
return 'View';
};
});
});
});
</script>
</body>
</html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 204 KiB

View File

@@ -1,28 +0,0 @@
import hashlib
import hmac
import random
import string
SECRET_KEY = "DebugTalk"
default_request = {
"base_url": "http://127.0.0.1:5000",
"headers": {
"Content-Type": "application/json",
"device_sn": "$device_sn"
}
}
def gen_random_string(str_len):
random_char_list = []
for _ in range(str_len):
random_char = random.choice(string.ascii_letters + string.digits)
random_char_list.append(random_char)
random_string = ''.join(random_char_list)
return random_string
def get_sign(*args):
content = ''.join(args).encode('ascii')
sign_key = SECRET_KEY.encode('ascii')
sign = hmac.new(sign_key, content, hashlib.sha1).hexdigest()
return sign

View File

@@ -1,7 +1,7 @@
__title__ = 'HttpRunner' __title__ = 'HttpRunner'
__description__ = 'One-stop solution for HTTP(S) testing.' __description__ = 'One-stop solution for HTTP(S) testing.'
__url__ = 'https://github.com/HttpRunner/HttpRunner' __url__ = 'https://github.com/HttpRunner/HttpRunner'
__version__ = '1.3.12' __version__ = '1.4.0.beta'
__author__ = 'debugtalk' __author__ = 'debugtalk'
__author_email__ = 'mail@debugtalk.com' __author_email__ = 'mail@debugtalk.com'
__license__ = 'MIT' __license__ = 'MIT'

View File

@@ -6,8 +6,7 @@ import os
import sys import sys
from httprunner.logger import color_print from httprunner.logger import color_print
from httprunner.testcase import load_test_file from httprunner.testcase import TestcaseLoader
from locust.main import main from locust.main import main
@@ -41,7 +40,7 @@ def gen_locustfile(testcase_file_path):
"templates", "templates",
"locustfile_template" "locustfile_template"
) )
testset = load_test_file(testcase_file_path) testset = TestcaseLoader.load_test_file(testcase_file_path)
host = testset.get("config", {}).get("request", {}).get("base_url", "") host = testset.get("config", {}).get("request", {}).get("base_url", "")
with io.open(template_path, encoding='utf-8') as template: with io.open(template_path, encoding='utf-8') as template:

View File

@@ -2,10 +2,11 @@
from unittest.case import SkipTest from unittest.case import SkipTest
from httprunner import exception, logger, response, testcase, utils from httprunner import exception, logger, response, utils
from httprunner.client import HttpSession from httprunner.client import HttpSession
from httprunner.context import Context from httprunner.context import Context
from httprunner.events import EventHook from httprunner.events import EventHook
from httprunner.testcase import TestcaseLoader
class Runner(object): class Runner(object):
@@ -13,7 +14,7 @@ class Runner(object):
def __init__(self, config_dict=None, http_client_session=None): def __init__(self, config_dict=None, http_client_session=None):
self.http_client_session = http_client_session self.http_client_session = http_client_session
self.context = Context() self.context = Context()
testcase.load_test_dependencies() TestcaseLoader.load_test_dependencies()
config_dict = config_dict or {} config_dict = config_dict or {}
self.init_config(config_dict, "testset") self.init_config(config_dict, "testset")

View File

@@ -7,6 +7,7 @@ import unittest
from httprunner import exception, logger, runner, testcase, utils from httprunner import exception, logger, runner, testcase, utils
from httprunner.compat import is_py3 from httprunner.compat import is_py3
from httprunner.report import HtmlTestResult, get_summary, render_html_report from httprunner.report import HtmlTestResult, get_summary, render_html_report
from httprunner.testcase import TestcaseLoader
class TestCase(unittest.TestCase): class TestCase(unittest.TestCase):
@@ -195,7 +196,7 @@ def init_task_suite(path_or_testsets, mapping=None, http_client_session=None):
""" initialize task suite """ initialize task suite
""" """
if not testcase.is_testsets(path_or_testsets): if not testcase.is_testsets(path_or_testsets):
testsets = testcase.load_testsets_by_path(path_or_testsets) testsets = TestcaseLoader.load_testsets_by_path(path_or_testsets)
else: else:
testsets = path_or_testsets testsets = path_or_testsets

View File

@@ -10,18 +10,12 @@ import random
import re import re
from httprunner import exception, logger, utils from httprunner import exception, logger, utils
from httprunner.utils import FileUtils
from httprunner.compat import OrderedDict, numeric_types from httprunner.compat import OrderedDict, numeric_types
from httprunner.utils import FileUtils
variable_regexp = r"\$([\w_]+)" variable_regexp = r"\$([\w_]+)"
function_regexp = r"\$\{([\w_]+\([\$\w\.\-_ =,]*\))\}" function_regexp = r"\$\{([\w_]+\([\$\w\.\-_ =,]*\))\}"
function_regexp_compile = re.compile(r"^([\w_]+)\(([\$\w\.\-_ =,]*)\)$") function_regexp_compile = re.compile(r"^([\w_]+)\(([\$\w\.\-_ =,]*)\)$")
test_def_overall_dict = {
"loaded": False,
"api": {},
"suite": {}
}
testcases_cache_mapping = {}
def extract_variables(content): def extract_variables(content):
@@ -86,6 +80,8 @@ def parse_function(content):
"kwargs": {} "kwargs": {}
} }
matched = function_regexp_compile.match(content) matched = function_regexp_compile.match(content)
if not matched:
raise exception.ApiNotFound("{} not found!".format(content))
function_meta["func_name"] = matched.group(1) function_meta["func_name"] = matched.group(1)
args_str = matched.group(2).replace(" ", "") args_str = matched.group(2).replace(" ", "")
@@ -102,86 +98,291 @@ def parse_function(content):
return function_meta return function_meta
def load_test_dependencies():
""" load all api and suite definitions.
default api folder is "$CWD/tests/api/".
default suite folder is "$CWD/tests/suite/".
"""
test_def_overall_dict["loaded"] = True
test_def_overall_dict["api"] = {}
test_def_overall_dict["suite"] = {}
# load api definitions class TestcaseLoader(object):
api_def_folder = os.path.join(os.getcwd(), "tests", "api")
api_files = FileUtils.load_folder_files(api_def_folder)
for test_file in api_files: overall_def_dict = {
testset = load_test_file(test_file) "api": {},
test_def_overall_dict["api"].update(testset["api"]) "suite": {}
}
testcases_cache_mapping = {}
# load suite definitions def load_test_dependencies():
suite_def_folder = os.path.join(os.getcwd(), "tests", "suite") """ load all api and suite definitions.
suite_files = FileUtils.load_folder_files(suite_def_folder) default api folder is "$CWD/tests/api/".
default suite folder is "$CWD/tests/suite/".
"""
# TODO: cache api and suite loading
# load api definitions
api_def_folder = os.path.join(os.getcwd(), "tests", "api")
for test_file in FileUtils.load_folder_files(api_def_folder):
TestcaseLoader.load_api_file(test_file)
for suite_file in suite_files: # load suite definitions
suite = load_test_file(suite_file) suite_def_folder = os.path.join(os.getcwd(), "tests", "suite")
if "def" not in suite["config"]: for suite_file in FileUtils.load_folder_files(suite_def_folder):
raise exception.ParamsError("def missed in suite file: {}!".format(suite_file)) suite = TestcaseLoader.load_test_file(suite_file)
if "def" not in suite["config"]:
raise exception.ParamsError("def missed in suite file: {}!".format(suite_file))
call_func = suite["config"]["def"] call_func = suite["config"]["def"]
function_meta = parse_function(call_func) function_meta = parse_function(call_func)
suite["function_meta"] = function_meta suite["function_meta"] = function_meta
test_def_overall_dict["suite"][function_meta["func_name"]] = suite TestcaseLoader.overall_def_dict["suite"][function_meta["func_name"]] = suite
def load_testsets_by_path(path): def load_api_file(file_path):
""" load testcases from file path """ load api definition from file and store in overall_def_dict["api"]
@param path: path could be in several type api file should be in format below:
- absolute/relative file path [
- absolute/relative folder path {
- list/set container with file(s) and/or folder(s) "api": {
@return testcase sets list, each testset is corresponding to a file "def": "api_login",
[ "request": {},
testset_dict_1, "validate": []
testset_dict_2 }
] },
""" {
if isinstance(path, (list, set)): "api": {
testsets = [] "def": "api_logout",
"request": {},
"validate": []
}
}
]
"""
api_items = FileUtils.load_file(file_path)
if not isinstance(api_items, list):
raise exception.FileFormatError("API format error: {}".format(file_path))
for file_path in set(path): for api_item in api_items:
testset = load_testsets_by_path(file_path) if not isinstance(api_item, dict) or len(api_item) != 1:
if not testset: raise exception.FileFormatError("API format error: {}".format(file_path))
continue
testsets.extend(testset)
return testsets key, api_dict = api_item.popitem()
if key != "api" or not isinstance(api_dict, dict) or "def" not in api_dict:
raise exception.FileFormatError("API format error: {}".format(file_path))
if not os.path.isabs(path): api_def = api_dict.pop("def")
path = os.path.join(os.getcwd(), path) function_meta = parse_function(api_def)
func_name = function_meta["func_name"]
if path in testcases_cache_mapping: if func_name in TestcaseLoader.overall_def_dict["api"]:
return testcases_cache_mapping[path] logger.log_warning("API definition duplicated: {}".format(func_name))
if os.path.isdir(path): api_dict["function_meta"] = function_meta
files_list = FileUtils.load_folder_files(path) TestcaseLoader.overall_def_dict["api"][func_name] = api_dict
testcases_list = load_testsets_by_path(files_list)
def load_test_file(file_path):
""" load testcase file or suite file
@param file_path: absolute valid file path
file_path should be in format below:
[
{
"config": {
"name": "",
"def": "suite_order()",
"request": {}
}
},
{
"test": {
"name": "add product to cart",
"api": "api_add_cart()",
"validate": []
}
},
{
"test": {
"name": "checkout cart",
"request": {},
"validate": []
}
}
]
@return testset dict
{
"name": "desc1",
"config": {},
"testcases": [testcase11, testcase12]
}
"""
testset = {
"name": "",
"config": {
"path": file_path
},
"testcases": [] # TODO: rename to tests
}
for item in FileUtils.load_file(file_path):
if not isinstance(item, dict) or len(item) != 1:
raise exception.FileFormatError("Testcase format error: {}".format(file_path))
key, test_block = item.popitem()
if not isinstance(test_block, dict):
raise exception.FileFormatError("Testcase format error: {}".format(file_path))
if key == "config":
testset["config"].update(test_block)
testset["name"] = test_block.get("name", "")
elif key == "test":
if "api" in test_block:
ref_call = test_block["api"]
def_block = TestcaseLoader._get_block_by_name(ref_call, "api")
TestcaseLoader._override_block(def_block, test_block)
testset["testcases"].append(test_block)
elif "suite" in test_block:
ref_call = test_block["suite"]
block = TestcaseLoader._get_block_by_name(ref_call, "suite")
testset["testcases"].extend(block["testcases"])
else:
testset["testcases"].append(test_block)
elif os.path.isfile(path):
try:
testset = load_test_file(path)
if testset["testcases"] or testset["api"]:
testcases_list = [testset]
else: else:
logger.log_warning(
"unexpected block key: {}. block key should only be 'config' or 'test'.".format(key)
)
return testset
def _get_block_by_name(ref_call, ref_type):
""" get test content by reference name
@params:
ref_call: e.g. api_v1_Account_Login_POST($UserName, $Password)
ref_type: "api" or "suite"
"""
function_meta = parse_function(ref_call)
func_name = function_meta["func_name"]
call_args = function_meta["args"]
block = TestcaseLoader._get_test_definition(func_name, ref_type)
def_args = block.get("function_meta").get("args", [])
if len(call_args) != len(def_args):
raise exception.ParamsError("call args mismatch defined args!")
args_mapping = {}
for index, item in enumerate(def_args):
if call_args[index] == item:
continue
args_mapping[item] = call_args[index]
if args_mapping:
block = substitute_variables_with_mapping(block, args_mapping)
return block
def _get_test_definition(name, ref_type):
""" get expected api or suite.
@params:
name: api or suite name
ref_type: "api" or "suite"
@return
expected api info if found, otherwise raise ApiNotFound exception
"""
block = TestcaseLoader.overall_def_dict.get(ref_type, {}).get(name)
if not block:
err_msg = "{} not found!".format(name)
if ref_type == "api":
raise exception.ApiNotFound(err_msg)
else:
# ref_type == "suite":
raise exception.SuiteNotFound(err_msg)
return block
def _override_block(def_block, current_block):
""" override def_block with current_block
@param def_block:
{
"name": "get token",
"request": {...},
"validate": [{'eq': ['status_code', 200]}]
}
@param current_block:
{
"name": "get token",
"extract": [{"token": "content.token"}],
"validate": [{'eq': ['status_code', 201]}, {'len_eq': ['content.token', 16]}]
}
@return
{
"name": "get token",
"request": {...},
"extract": [{"token": "content.token"}],
"validate": [{'eq': ['status_code', 201]}, {'len_eq': ['content.token', 16]}]
}
"""
def_validators = def_block.get("validate") or def_block.get("validators", [])
current_validators = current_block.get("validate") or current_block.get("validators", [])
def_extrators = def_block.get("extract") \
or def_block.get("extractors") \
or def_block.get("extract_binds", [])
current_extractors = current_block.get("extract") \
or current_block.get("extractors") \
or current_block.get("extract_binds", [])
current_block.update(def_block)
current_block["validate"] = _merge_validator(
def_validators,
current_validators
)
current_block["extract"] = _merge_extractor(
def_extrators,
current_extractors
)
def load_testsets_by_path(path):
""" load testcases from file path
@param path: path could be in several type
- absolute/relative file path
- absolute/relative folder path
- list/set container with file(s) and/or folder(s)
@return testcase sets list, each testset is corresponding to a file
[
testset_dict_1,
testset_dict_2
]
"""
if isinstance(path, (list, set)):
testsets = []
for file_path in set(path):
testset = TestcaseLoader.load_testsets_by_path(file_path)
if not testset:
continue
testsets.extend(testset)
return testsets
if not os.path.isabs(path):
path = os.path.join(os.getcwd(), path)
if path in TestcaseLoader.testcases_cache_mapping:
return TestcaseLoader.testcases_cache_mapping[path]
if os.path.isdir(path):
files_list = FileUtils.load_folder_files(path)
testcases_list = TestcaseLoader.load_testsets_by_path(files_list)
elif os.path.isfile(path):
try:
testset = TestcaseLoader.load_test_file(path)
if testset["testcases"] or testset["api"]:
testcases_list = [testset]
else:
testcases_list = []
except exception.FileFormatError:
testcases_list = [] testcases_list = []
except exception.FileFormatError:
else:
logger.log_error(u"file not found: {}".format(path))
testcases_list = [] testcases_list = []
else: TestcaseLoader.testcases_cache_mapping[path] = testcases_list
logger.log_error(u"file not found: {}".format(path)) return testcases_list
testcases_list = []
testcases_cache_mapping[path] = testcases_list
return testcases_list
def parse_validator(validator): def parse_validator(validator):
""" parse validator, validator maybe in two format """ parse validator, validator maybe in two format
@@ -262,11 +463,11 @@ def _get_validators_mapping(validators):
return validators_mapping return validators_mapping
def merge_validator(api_validators, test_validators): def _merge_validator(def_validators, current_validators):
""" merge api_validators with test_validators """ merge def_validators with current_validators
@params: @params:
api_validators: [{'eq': ['v1', 200]}, {"check": "s2", "expect": 16, "comparator": "len_eq"}] def_validators: [{'eq': ['v1', 200]}, {"check": "s2", "expect": 16, "comparator": "len_eq"}]
test_validators: [{"check": "v1", "expect": 201}, {'len_eq': ['s3', 12]}] current_validators: [{"check": "v1", "expect": 201}, {'len_eq': ['s3', 12]}]
@return: @return:
[ [
{"check": "v1", "expect": 201, "comparator": "eq"}, {"check": "v1", "expect": 201, "comparator": "eq"},
@@ -274,24 +475,24 @@ def merge_validator(api_validators, test_validators):
{"check": "s3", "expect": 12, "comparator": "len_eq"} {"check": "s3", "expect": 12, "comparator": "len_eq"}
] ]
""" """
if not api_validators: if not def_validators:
return test_validators return current_validators
elif not test_validators: elif not current_validators:
return api_validators return def_validators
else: else:
api_validators_mapping = _get_validators_mapping(api_validators) api_validators_mapping = _get_validators_mapping(def_validators)
test_validators_mapping = _get_validators_mapping(test_validators) test_validators_mapping = _get_validators_mapping(current_validators)
api_validators_mapping.update(test_validators_mapping) api_validators_mapping.update(test_validators_mapping)
return list(api_validators_mapping.values()) return list(api_validators_mapping.values())
def merge_extractor(api_extrators, test_extracors): def _merge_extractor(def_extrators, current_extractors):
""" merge api_extrators with test_extracors """ merge def_extrators with current_extractors
@params: @params:
api_extrators: [{"var1": "val1"}, {"var2": "val2"}] def_extrators: [{"var1": "val1"}, {"var2": "val2"}]
test_extracors: [{"var1": "val111"}, {"var3": "val3"}] current_extractors: [{"var1": "val111"}, {"var3": "val3"}]
@return: @return:
[ [
{"var1": "val111"}, {"var1": "val111"},
@@ -299,15 +500,15 @@ def merge_extractor(api_extrators, test_extracors):
{"var3": "val3"} {"var3": "val3"}
] ]
""" """
if not api_extrators: if not def_extrators:
return test_extracors return current_extractors
elif not test_extracors: elif not current_extractors:
return api_extrators return def_extrators
else: else:
extractor_dict = OrderedDict() extractor_dict = OrderedDict()
for api_extrator in api_extrators: for api_extrator in def_extrators:
if len(api_extrator) != 1: if len(api_extrator) != 1:
logger.log_warning("incorrect extractor: {}".format(api_extrator)) logger.log_warning("incorrect extractor: {}".format(api_extrator))
continue continue
@@ -315,7 +516,7 @@ def merge_extractor(api_extrators, test_extracors):
var_name = list(api_extrator.keys())[0] var_name = list(api_extrator.keys())[0]
extractor_dict[var_name] = api_extrator[var_name] extractor_dict[var_name] = api_extrator[var_name]
for test_extrator in test_extracors: for test_extrator in current_extractors:
if len(test_extrator) != 1: if len(test_extrator) != 1:
logger.log_warning("incorrect extractor: {}".format(test_extrator)) logger.log_warning("incorrect extractor: {}".format(test_extrator))
continue continue
@@ -329,46 +530,6 @@ def merge_extractor(api_extrators, test_extracors):
return extractor_list return extractor_list
def extend_test_api(test_block_dict):
""" update test block api with api definition
@param
test_block_dict:
{
"name": "get token",
"api": "get_token($user_agent, $device_sn, $os_platform, $app_version)",
"extract": [{"token": "content.token"}],
"validate": [{'eq': ['status_code', 200]}, {'len_eq': ['content.token', 16]}]
}
@return
{
"name": "get token",
"request": {...},
"extract": [{"token": "content.token"}],
"validate": [{'eq': ['status_code', 200]}, {'len_eq': ['content.token', 16]}]
}
"""
ref_name = test_block_dict["api"]
test_info = get_testinfo_by_reference(ref_name, "api")
api_validators = test_info.get("validate") or test_info.get("validators", [])
test_validators = test_block_dict.get("validate") or test_block_dict.get("validators", [])
api_extrators = test_info.get("extract") \
or test_info.get("extractors") \
or test_info.get("extract_binds", [])
test_extracors = test_block_dict.get("extract") \
or test_block_dict.get("extractors") \
or test_block_dict.get("extract_binds", [])
test_block_dict.update(test_info)
test_block_dict["validate"] = merge_validator(
api_validators,
test_validators
)
test_block_dict["extract"] = merge_extractor(
api_extrators,
test_extracors
)
def is_testset(data_structure): def is_testset(data_structure):
""" check if data_structure is a testset """ check if data_structure is a testset
@@ -410,115 +571,6 @@ def is_testsets(data_structure):
return True return True
def load_test_file(file_path):
""" load testset file, get testset data structure.
@param file_path: absolute valid testset file path
@return testset dict
{
"name": "desc1",
"config": {},
"api": {},
"testcases": [testcase11, testcase12]
}
"""
testset = {
"name": "",
"config": {
"path": file_path
},
"api": {},
"testcases": []
}
tests_list = FileUtils.load_file(file_path)
for item in tests_list:
for key in item:
if key == "config":
testset["config"].update(item["config"])
testset["name"] = item["config"].get("name", "")
elif key == "test":
test_block_dict = item["test"]
if "api" in test_block_dict:
extend_test_api(test_block_dict)
testset["testcases"].append(test_block_dict)
elif "suite" in test_block_dict:
ref_name = test_block_dict["suite"]
test_info = get_testinfo_by_reference(ref_name, "suite")
testset["testcases"].extend(test_info["testcases"])
else:
testset["testcases"].append(test_block_dict)
elif key == "api":
api_def = item["api"].pop("def")
function_meta = parse_function(api_def)
func_name = function_meta["func_name"]
if func_name in testset["api"]:
logger.log_warning("api definition duplicated: {}".format(func_name))
api_info = {}
api_info["function_meta"] = function_meta
api_info.update(item["api"])
testset["api"][func_name] = api_info
else:
logger.log_warning(
"unexpected block: {}. block should only be 'config', 'test' or 'api'.".format(key)
)
return testset
def get_testinfo_by_reference(ref_name, ref_type):
""" get test content by reference name
@params:
ref_name: reference name, e.g. api_v1_Account_Login_POST($UserName, $Password)
ref_type: "api" or "suite"
"""
function_meta = parse_function(ref_name)
func_name = function_meta["func_name"]
call_args = function_meta["args"]
test_info = get_test_definition(func_name, ref_type)
def_args = test_info.get("function_meta").get("args", [])
if len(call_args) != len(def_args):
raise exception.ParamsError("call args mismatch defined args!")
args_mapping = {}
for index, item in enumerate(def_args):
if call_args[index] == item:
continue
args_mapping[item] = call_args[index]
if args_mapping:
test_info = substitute_variables_with_mapping(test_info, args_mapping)
return test_info
def get_test_definition(name, ref_type):
""" get expected api or suite.
@params:
name: api or suite name
ref_type: "api" or "suite"
@return
expected api info if found, otherwise raise ApiNotFound exception
"""
if not test_def_overall_dict.get("loaded", False):
load_test_dependencies()
test_info = test_def_overall_dict.get(ref_type, {}).get(name)
if not test_info:
err_msg = "{} {} not found!".format(ref_type, name)
if ref_type == "api":
raise exception.ApiNotFound(err_msg)
elif ref_type == "suite":
raise exception.SuiteNotFound(err_msg)
else:
raise exception.ParamsError("ref_type can only be api or suite!")
return test_info
def substitute_variables_with_mapping(content, mapping): def substitute_variables_with_mapping(content, mapping):
""" substitute variables in content with mapping """ substitute variables in content with mapping
e.g. e.g.

View File

@@ -11,8 +11,9 @@
json: json:
sign: ${get_sign($user_agent, $device_sn, $os_platform, $app_version)} sign: ${get_sign($user_agent, $device_sn, $os_platform, $app_version)}
validate: validate:
- eq: ["status_code", 200] - eq: ["status_code", 0]
- len_eq: ["content.token", 16] - len_eq: ["content.token", 12]
- contains: [{"a": 1, "b": 2}, "a"]
- api: - api:
def: create_user($uid, $user_name, $user_password, $token) def: create_user($uid, $user_name, $user_password, $token)

View File

@@ -1,70 +0,0 @@
- api:
def: get_token($user_agent, $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)}
validate:
- "eq": ["status_code", 0]
- "len_eq": ["content.token", 12]
- "contains": [{"a": 1, "b": 2}, "a"]
- 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
- api:
def: get_user($uid, $token)
request:
url: /api/users/$uid
method: GET
headers:
token: $token
- api:
def: update_user($uid, $user_name, $user_password, $token)
request:
url: /api/users/$uid
method: PUT
headers:
token: $token
json:
name: $user_name
password: $user_password
- api:
def: delete_user($uid, $token)
request:
url: /api/users/$uid
method: DELETE
headers:
token: $token
- api:
def: get_users($token)
request:
url: /api/users
method: GET
headers:
token: $token
- api:
def: reset_all($token)
request:
url: /api/reset-all
method: GET
headers:
token: $token

View File

@@ -1,7 +1,8 @@
import os import os
import time import time
from httprunner import HttpRunner, exception, runner, testcase from httprunner import HttpRunner, exception, runner
from httprunner.testcase import TestcaseLoader
from httprunner.utils import FileUtils, deep_update_dict from httprunner.utils import FileUtils, deep_update_dict
from tests.base import ApiServerUnittest from tests.base import ApiServerUnittest
@@ -154,7 +155,7 @@ class TestRunner(ApiServerUnittest):
def test_run_testcase_with_empty_header(self): def test_run_testcase_with_empty_header(self):
testcase_file_path = os.path.join( testcase_file_path = os.path.join(
os.getcwd(), 'tests/data/test_bugfix.yml') os.getcwd(), 'tests/data/test_bugfix.yml')
testsets = testcase.load_testsets_by_path(testcase_file_path) testsets = TestcaseLoader.load_testsets_by_path(testcase_file_path)
testset = testsets[0] testset = testsets[0]
config_dict_headers = testset["config"]["request"]["headers"] config_dict_headers = testset["config"]["request"]["headers"]
test_dict_headers = testset["testcases"][0]["request"]["headers"] test_dict_headers = testset["testcases"][0]["request"]["headers"]

View File

@@ -1,7 +1,7 @@
import os import os
from httprunner import task from httprunner import task
from httprunner.testcase import load_test_file from httprunner.testcase import TestcaseLoader
from tests.base import ApiServerUnittest from tests.base import ApiServerUnittest
@@ -17,7 +17,7 @@ class TestTask(ApiServerUnittest):
def test_create_suite(self): def test_create_suite(self):
testcase_file_path = os.path.join(os.getcwd(), 'tests/data/demo_testset_variables.yml') testcase_file_path = os.path.join(os.getcwd(), 'tests/data/demo_testset_variables.yml')
testset = load_test_file(testcase_file_path) testset = TestcaseLoader.load_test_file(testcase_file_path)
suite = task.TestSuite(testset) suite = task.TestSuite(testset)
self.assertEqual(suite.countTestCases(), 3) self.assertEqual(suite.countTestCases(), 3)
for testcase in suite: for testcase in suite:

View File

@@ -4,7 +4,192 @@ import unittest
from httprunner import testcase from httprunner import testcase
from httprunner.exception import (ApiNotFound, FileFormatError, from httprunner.exception import (ApiNotFound, FileFormatError,
FileNotFoundError, ParamsError) FileNotFoundError, ParamsError,
SuiteNotFound)
from httprunner.testcase import TestcaseLoader
class TestTestcaseLoader(unittest.TestCase):
def setUp(self):
TestcaseLoader.overall_def_dict = {
"api": {},
"suite": {}
}
def test_load_test_dependencies(self):
TestcaseLoader.load_test_dependencies()
overall_def_dict = TestcaseLoader.overall_def_dict
self.assertIn("get_token", overall_def_dict["api"])
self.assertIn("create_and_check", overall_def_dict["suite"])
def test_load_api_file(self):
TestcaseLoader.load_api_file("tests/api/basic.yml")
overall_api_def_dict = TestcaseLoader.overall_def_dict["api"]
self.assertIn("get_token",overall_api_def_dict)
self.assertEqual("/api/get-token", overall_api_def_dict["get_token"]["request"]["url"])
self.assertIn("$user_agent", overall_api_def_dict["get_token"]["function_meta"]["args"])
self.assertEqual(len(overall_api_def_dict["get_token"]["validate"]), 3)
def test_load_test_file_suite(self):
TestcaseLoader.load_api_file("tests/api/basic.yml")
testset = TestcaseLoader.load_test_file("tests/suite/create_and_get.yml")
self.assertEqual(testset["name"], "create user and check result.")
self.assertEqual(testset["config"]["name"], "create user and check result.")
self.assertEqual(len(testset["testcases"]), 3)
self.assertEqual(testset["testcases"][0]["name"], "make sure user $uid does not exist")
self.assertEqual(testset["testcases"][0]["request"]["url"], "/api/users/$uid")
def test_load_test_file_testcase(self):
TestcaseLoader.load_test_dependencies()
testset = TestcaseLoader.load_test_file("tests/testcases/smoketest.yml")
self.assertEqual(testset["name"], "smoketest")
self.assertEqual(testset["config"]["path"], "tests/testcases/smoketest.yml")
self.assertIn("device_sn", testset["config"]["variables"][0])
self.assertEqual(len(testset["testcases"]), 8)
self.assertEqual(testset["testcases"][0]["name"], "get token")
def test_get_block_by_name(self):
TestcaseLoader.load_test_dependencies()
ref_call = "get_user($uid, $token)"
block = TestcaseLoader._get_block_by_name(ref_call, "api")
self.assertEqual(block["request"]["url"], "/api/users/$uid")
self.assertEqual(block["function_meta"]["func_name"], "get_user")
self.assertEqual(block["function_meta"]["args"], ['$uid', '$token'])
def test_get_block_by_name_args_mismatch(self):
TestcaseLoader.load_test_dependencies()
ref_call = "get_user($uid, $token, $var)"
with self.assertRaises(ParamsError):
TestcaseLoader._get_block_by_name(ref_call, "api")
def test_get_test_definition_api(self):
TestcaseLoader.load_test_dependencies()
api_def = TestcaseLoader._get_test_definition("get_token", "api")
self.assertEqual(api_def["request"]["url"], "/api/get-token")
with self.assertRaises(ApiNotFound):
TestcaseLoader._get_test_definition("get_token_XXX", "api")
def test_get_test_definition_suite(self):
TestcaseLoader.load_test_dependencies()
api_def = TestcaseLoader._get_test_definition("create_and_check", "suite")
self.assertEqual(api_def["name"], "create user and check result.")
with self.assertRaises(SuiteNotFound):
TestcaseLoader._get_test_definition("create_and_check_XXX", "suite")
def test_override_block(self):
TestcaseLoader.load_test_dependencies()
def_block = TestcaseLoader._get_block_by_name("get_token($user_agent, $device_sn, $os_platform, $app_version)", "api")
test_block = {
"name": "override block",
"variables": [
{"var": 123}
],
'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)}'}},
'validate': [
{'eq': ['status_code', 201]},
{'len_eq': ['content.token', 32]}
]
}
TestcaseLoader._override_block(def_block, test_block)
self.assertEqual(test_block["name"], "override block")
self.assertEqual(test_block["validate"][0], {'check': 'status_code', 'expect': 201, 'comparator': 'eq'})
self.assertEqual(test_block["validate"][1], {'check': 'content.token', 'comparator': 'len_eq', 'expect': 32})
def test_load_testcases_by_path_files(self):
testsets_list = []
# absolute file path
path = os.path.join(
os.getcwd(), 'tests/data/demo_testset_hardcode.json')
testset_list = TestcaseLoader.load_testsets_by_path(path)
self.assertEqual(len(testset_list), 1)
self.assertIn("path", testset_list[0]["config"])
self.assertEqual(testset_list[0]["config"]["path"], path)
self.assertEqual(len(testset_list[0]["testcases"]), 3)
testsets_list.extend(testset_list)
# relative file path
path = 'tests/data/demo_testset_hardcode.yml'
testset_list = TestcaseLoader.load_testsets_by_path(path)
self.assertEqual(len(testset_list), 1)
self.assertIn("path", testset_list[0]["config"])
self.assertIn(path, testset_list[0]["config"]["path"])
self.assertEqual(len(testset_list[0]["testcases"]), 3)
testsets_list.extend(testset_list)
# list/set container with file(s)
path = [
os.path.join(os.getcwd(), 'tests/data/demo_testset_hardcode.json'),
'tests/data/demo_testset_hardcode.yml'
]
testset_list = TestcaseLoader.load_testsets_by_path(path)
self.assertEqual(len(testset_list), 2)
self.assertEqual(len(testset_list[0]["testcases"]), 3)
self.assertEqual(len(testset_list[1]["testcases"]), 3)
testsets_list.extend(testset_list)
self.assertEqual(len(testsets_list), 4)
for testset in testsets_list:
for test in testset["testcases"]:
self.assertIn('name', test)
self.assertIn('request', test)
self.assertIn('url', test['request'])
self.assertIn('method', test['request'])
def test_load_testcases_by_path_folder(self):
TestcaseLoader.load_test_dependencies()
# absolute folder path
path = os.path.join(os.getcwd(), 'tests/data')
testset_list_1 = TestcaseLoader.load_testsets_by_path(path)
self.assertGreater(len(testset_list_1), 4)
# relative folder path
path = 'tests/data/'
testset_list_2 = TestcaseLoader.load_testsets_by_path(path)
self.assertEqual(len(testset_list_1), len(testset_list_2))
# list/set container with file(s)
path = [
os.path.join(os.getcwd(), 'tests/data'),
'tests/data/'
]
testset_list_3 = TestcaseLoader.load_testsets_by_path(path)
self.assertEqual(len(testset_list_3), 2 * len(testset_list_1))
def test_load_testcases_by_path_not_exist(self):
# absolute folder path
path = os.path.join(os.getcwd(), 'tests/data_not_exist')
testset_list_1 = TestcaseLoader.load_testsets_by_path(path)
self.assertEqual(testset_list_1, [])
# relative folder path
path = 'tests/data_not_exist'
testset_list_2 = TestcaseLoader.load_testsets_by_path(path)
self.assertEqual(testset_list_2, [])
# list/set container with file(s)
path = [
os.path.join(os.getcwd(), 'tests/data_not_exist'),
'tests/data_not_exist/'
]
testset_list_3 = TestcaseLoader.load_testsets_by_path(path)
self.assertEqual(testset_list_3, [])
def test_load_testcases_by_path_layered(self):
TestcaseLoader.load_test_dependencies()
path = os.path.join(
os.getcwd(), 'tests/data/demo_testset_layer.yml')
testsets_list = TestcaseLoader.load_testsets_by_path(path)
self.assertIn("variables", testsets_list[0]["config"])
self.assertIn("request", testsets_list[0]["config"])
self.assertIn("request", testsets_list[0]["testcases"][0])
self.assertIn("url", testsets_list[0]["testcases"][0]["request"])
self.assertIn("validate", testsets_list[0]["testcases"][0])
class TestcaseParserUnittest(unittest.TestCase): class TestcaseParserUnittest(unittest.TestCase):
@@ -448,94 +633,6 @@ class TestcaseParserUnittest(unittest.TestCase):
3 3
) )
def test_load_testcases_by_path_files(self):
testsets_list = []
# absolute file path
path = os.path.join(
os.getcwd(), 'tests/data/demo_testset_hardcode.json')
testset_list = testcase.load_testsets_by_path(path)
self.assertEqual(len(testset_list), 1)
self.assertIn("path", testset_list[0]["config"])
self.assertEqual(testset_list[0]["config"]["path"], path)
self.assertEqual(len(testset_list[0]["testcases"]), 3)
testsets_list.extend(testset_list)
# relative file path
path = 'tests/data/demo_testset_hardcode.yml'
testset_list = testcase.load_testsets_by_path(path)
self.assertEqual(len(testset_list), 1)
self.assertIn("path", testset_list[0]["config"])
self.assertIn(path, testset_list[0]["config"]["path"])
self.assertEqual(len(testset_list[0]["testcases"]), 3)
testsets_list.extend(testset_list)
# list/set container with file(s)
path = [
os.path.join(os.getcwd(), 'tests/data/demo_testset_hardcode.json'),
'tests/data/demo_testset_hardcode.yml'
]
testset_list = testcase.load_testsets_by_path(path)
self.assertEqual(len(testset_list), 2)
self.assertEqual(len(testset_list[0]["testcases"]), 3)
self.assertEqual(len(testset_list[1]["testcases"]), 3)
testsets_list.extend(testset_list)
self.assertEqual(len(testsets_list), 4)
for testset in testsets_list:
for test in testset["testcases"]:
self.assertIn('name', test)
self.assertIn('request', test)
self.assertIn('url', test['request'])
self.assertIn('method', test['request'])
def test_load_testcases_by_path_folder(self):
# absolute folder path
path = os.path.join(os.getcwd(), 'tests/data')
testset_list_1 = testcase.load_testsets_by_path(path)
self.assertGreater(len(testset_list_1), 4)
# relative folder path
path = 'tests/data/'
testset_list_2 = testcase.load_testsets_by_path(path)
self.assertEqual(len(testset_list_1), len(testset_list_2))
# list/set container with file(s)
path = [
os.path.join(os.getcwd(), 'tests/data'),
'tests/data/'
]
testset_list_3 = testcase.load_testsets_by_path(path)
self.assertEqual(len(testset_list_3), 2 * len(testset_list_1))
def test_load_testcases_by_path_not_exist(self):
# absolute folder path
path = os.path.join(os.getcwd(), 'tests/data_not_exist')
testset_list_1 = testcase.load_testsets_by_path(path)
self.assertEqual(testset_list_1, [])
# relative folder path
path = 'tests/data_not_exist'
testset_list_2 = testcase.load_testsets_by_path(path)
self.assertEqual(testset_list_2, [])
# list/set container with file(s)
path = [
os.path.join(os.getcwd(), 'tests/data_not_exist'),
'tests/data_not_exist/'
]
testset_list_3 = testcase.load_testsets_by_path(path)
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 = testcase.load_testsets_by_path(path)
self.assertIn("variables", testsets_list[0]["config"])
self.assertIn("request", testsets_list[0]["config"])
self.assertIn("request", testsets_list[0]["testcases"][0])
self.assertIn("url", testsets_list[0]["testcases"][0]["request"])
self.assertIn("validate", testsets_list[0]["testcases"][0])
def test_substitute_variables_with_mapping(self): def test_substitute_variables_with_mapping(self):
content = { content = {
@@ -564,23 +661,6 @@ class TestcaseParserUnittest(unittest.TestCase):
self.assertFalse(result["request"]["data"]["false"]) self.assertFalse(result["request"]["data"]["false"])
self.assertEqual("", result["request"]["data"]["empty_str"]) self.assertEqual("", result["request"]["data"]["empty_str"])
def test_load_test_dependencies(self):
testcase.test_def_overall_dict = {}
testcase.load_test_dependencies()
self.assertTrue(testcase.test_def_overall_dict["loaded"])
api_dict = testcase.test_def_overall_dict["api"]
self.assertIn("get_token", api_dict)
self.assertEqual("/api/get-token", api_dict["get_token"]["request"]["url"])
self.assertIn("$user_agent", api_dict["get_token"]["function_meta"]["args"])
self.assertIn("create_user", api_dict)
def test_get_api_definition(self):
api_info = testcase.get_test_definition("get_token", "api")
self.assertEqual("/api/get-token", api_info["request"]["url"])
self.assertIn("get_token", testcase.test_def_overall_dict["api"])
with self.assertRaises(ApiNotFound):
testcase.get_test_definition("api_not_exist", "api")
def test_parse_validator(self): def test_parse_validator(self):
validator = {"check": "status_code", "comparator": "eq", "expect": 201} validator = {"check": "status_code", "comparator": "eq", "expect": 201}
@@ -596,16 +676,16 @@ class TestcaseParserUnittest(unittest.TestCase):
) )
def test_merge_validator(self): def test_merge_validator(self):
api_validators = [ def_validators = [
{'eq': ['v1', 200]}, {'eq': ['v1', 200]},
{"check": "s2", "expect": 16, "comparator": "len_eq"} {"check": "s2", "expect": 16, "comparator": "len_eq"}
] ]
test_validators = [ current_validators = [
{"check": "v1", "expect": 201}, {"check": "v1", "expect": 201},
{'len_eq': ['s3', 12]} {'len_eq': ['s3', 12]}
] ]
merged_validators = testcase.merge_validator(api_validators, test_validators) merged_validators = testcase._merge_validator(def_validators, current_validators)
self.assertIn( self.assertIn(
{"check": "v1", "expect": 201, "comparator": "eq"}, {"check": "v1", "expect": 201, "comparator": "eq"},
merged_validators merged_validators
@@ -620,25 +700,25 @@ class TestcaseParserUnittest(unittest.TestCase):
) )
def test_merge_validator_with_dict(self): def test_merge_validator_with_dict(self):
api_validators = [ def_validators = [
{'eq': ["a", {"v": 1}]}, {'eq': ["a", {"v": 1}]},
{'eq': [{"b": 1}, 200]} {'eq': [{"b": 1}, 200]}
] ]
test_validators = [ current_validators = [
{'len_eq': ['s3', 12]}, {'len_eq': ['s3', 12]},
{'eq': [{"b": 1}, 201]} {'eq': [{"b": 1}, 201]}
] ]
merged_validators = testcase.merge_validator(api_validators, test_validators) merged_validators = testcase._merge_validator(def_validators, current_validators)
self.assertEqual(len(merged_validators), 3) self.assertEqual(len(merged_validators), 3)
self.assertIn({'check': {'b': 1}, 'expect': 201, 'comparator': 'eq'}, merged_validators) self.assertIn({'check': {'b': 1}, 'expect': 201, 'comparator': 'eq'}, merged_validators)
self.assertNotIn({'check': {'b': 1}, 'expect': 200, 'comparator': 'eq'}, merged_validators) self.assertNotIn({'check': {'b': 1}, 'expect': 200, 'comparator': 'eq'}, merged_validators)
def test_merge_extractor(self): def test_merge_extractor(self):
api_extrators = [{"var1": "val1"}, {"var2": "val2"}] api_extrators = [{"var1": "val1"}, {"var2": "val2"}]
test_extracors = [{"var1": "val111"}, {"var3": "val3"}] current_extractors = [{"var1": "val111"}, {"var3": "val3"}]
merged_extractors = testcase.merge_extractor(api_extrators, test_extracors) merged_extractors = testcase._merge_extractor(api_extrators, current_extractors)
self.assertIn( self.assertIn(
{"var1": "val111"}, {"var1": "val111"},
merged_extractors merged_extractors

View File

@@ -126,11 +126,11 @@ class TestFileUtils(unittest.TestCase):
self.assertNotIn(file1, files) self.assertNotIn(file1, files)
files_1 = FileUtils.load_folder_files(folder) files_1 = FileUtils.load_folder_files(folder)
api_file = os.path.join(os.getcwd(), 'tests', 'api', 'demo.yml') api_file = os.path.join(os.getcwd(), 'tests', 'api', 'basic.yml')
self.assertEqual(files_1[0], api_file) self.assertEqual(files_1[0], api_file)
files_2 = FileUtils.load_folder_files(folder) files_2 = FileUtils.load_folder_files(folder)
api_file = os.path.join(os.getcwd(), 'tests', 'api', 'demo.yml') api_file = os.path.join(os.getcwd(), 'tests', 'api', 'basic.yml')
self.assertEqual(files_2[0], api_file) self.assertEqual(files_2[0], api_file)
self.assertEqual(len(files_1), len(files_2)) self.assertEqual(len(files_1), len(files_2))
@@ -231,7 +231,7 @@ class TestUtils(ApiServerUnittest):
self.assertEqual(utils.get_uniform_comparator("count_le"), "length_less_than_or_equals") self.assertEqual(utils.get_uniform_comparator("count_le"), "length_less_than_or_equals")
self.assertEqual(utils.get_uniform_comparator("count_less_than_or_equals"), "length_less_than_or_equals") self.assertEqual(utils.get_uniform_comparator("count_less_than_or_equals"), "length_less_than_or_equals")
def test_validators(self): def current_validators(self):
imported_module = utils.get_imported_module("httprunner.built_in") imported_module = utils.get_imported_module("httprunner.built_in")
functions_mapping = utils.filter_module(imported_module, "function") functions_mapping = utils.filter_module(imported_module, "function")