change: python testcase template

This commit is contained in:
debugtalk
2020-05-14 22:41:35 +08:00
parent ba0611a09a
commit f88845d9d4
13 changed files with 575 additions and 363 deletions
@@ -1,83 +1,79 @@
# NOTICE: Generated By HttpRunner. DO'NOT EDIT!
import unittest
from httprunner.runner import TestCaseRunner from httprunner.runner import TestCaseRunner
from httprunner.schema import TestsConfig, TestStep from httprunner.schema import TestsConfig, TestStep
class TestCaseRequestMethodsHardcode(TestCaseRunner): class TestCaseHardcode(unittest.TestCase):
config = TestsConfig(**{ config = TestsConfig(
"name": "request methods testcase in hardcode", **{
"base_url": "https://postman-echo.com", "name": "request methods testcase in hardcode",
"verify": False "base_url": "https://postman-echo.com",
}) "verify": False,
"path": "examples/postman_echo/request_methods/hardcode_test.py",
}
)
teststeps = [ teststeps = [
TestStep(**{ TestStep(
"name": "get with params", **{
"request": { "name": "get with params",
"method": "GET", "request": {
"url": "/get", "method": "GET",
"params": { "url": "/get",
"foo1": "bar1", "params": {"foo1": "bar1", "foo2": "bar2"},
"foo2": "bar2" "headers": {"User-Agent": "HttpRunner/3.0"},
}, },
"headers": { "validate": [{"eq": ["status_code", 200]}],
"User-Agent": "HttpRunner/3.0" }
} ),
}, TestStep(
"extract": { **{
"server": "headers.Server" "name": "post raw text",
}, "request": {
"validate": [ "method": "POST",
{"eq": ["status_code", 200]}, "url": "/post",
{"eq": ["headers.Server", "nginx"]} "headers": {
] "User-Agent": "HttpRunner/3.0",
}), "Content-Type": "text/plain",
TestStep(**{ },
"name": "post raw text", "data": "This is expected to be sent back as part of response body.",
"request": { },
"method": "POST", "validate": [{"eq": ["status_code", 200]}],
"url": "/post", }
"data": "This is expected to be sent back as part of response body.", ),
"headers": { TestStep(
"User-Agent": "HttpRunner/3.0", **{
"Content-Type": "text/plain" "name": "post form data",
} "request": {
}, "method": "POST",
"validate": [ "url": "/post",
{"eq": ["status_code", 200]} "headers": {
] "User-Agent": "HttpRunner/3.0",
}), "Content-Type": "application/x-www-form-urlencoded",
TestStep(**{ },
"name": "post form data", "data": "foo1=bar1&foo2=bar2",
"request": { },
"method": "POST", "validate": [{"eq": ["status_code", 200]}],
"url": "/post", }
"data": "foo1=bar1&foo2=bar2", ),
"headers": { TestStep(
"User-Agent": "HttpRunner/3.0", **{
"Content-Type": "application/x-www-form-urlencoded" "name": "put request",
} "request": {
}, "method": "PUT",
"validate": [ "url": "/put",
{"eq": ["status_code", 200]} "headers": {
] "User-Agent": "HttpRunner/3.0",
}), "Content-Type": "text/plain",
TestStep(**{ },
"name": "put request", "data": "This is expected to be sent back as part of response body.",
"request": { },
"method": "PUT", "validate": [{"eq": ["status_code", 200]}],
"url": "/put", }
"data": "This is expected to be sent back as part of response body.", ),
"headers": {
"User-Agent": "HttpRunner/3.0",
"Content-Type": "text/plain"
}
},
"validate": [
{"eq": ["status_code", 200]}
]
})
] ]
def test_start(self):
if __name__ == '__main__': TestCaseRunner(self.config, self.teststeps).run()
TestCaseRequestMethodsHardcode().run()
@@ -1,98 +1,90 @@
# NOTICE: Generated By HttpRunner. DO'NOT EDIT!
import unittest
from httprunner.runner import TestCaseRunner from httprunner.runner import TestCaseRunner
from httprunner.schema import TestsConfig, TestStep from httprunner.schema import TestsConfig, TestStep
from examples.postman_echo import debugtalk
class TestCaseRequestMethodsWithFunctions(TestCaseRunner): class TestCaseRequestWithFunctions(unittest.TestCase):
config = TestsConfig(**{ config = TestsConfig(
"name": "request methods testcase with functions", **{
"variables": { "name": "request methods testcase with functions",
"foo1": "session_bar1" "variables": {"foo1": "session_bar1"},
}, "base_url": "https://postman-echo.com",
"functions": { "verify": False,
"get_httprunner_version": debugtalk.get_httprunner_version, "path": "examples/postman_echo/request_methods/request_with_functions_test.py",
"sum_two": debugtalk.sum_two }
}, )
"base_url": "https://postman-echo.com",
"verify": False
})
teststeps = [ teststeps = [
TestStep(**{ TestStep(
"name": "get with params", **{
"variables": { "name": "get with params",
"foo1": "bar1", "variables": {
"foo2": "session_bar2", "foo1": "bar1",
"sum_v": "${sum_two(1, 2)}" "foo2": "session_bar2",
}, "sum_v": "${sum_two(1, 2)}",
"request": {
"method": "GET",
"url": "/get",
"params": {
"foo1": "$foo1",
"foo2": "$foo2",
"sum_v": "$sum_v"
}, },
"headers": { "request": {
"User-Agent": "HttpRunner/${get_httprunner_version()}" "method": "GET",
} "url": "/get",
}, "params": {"foo1": "$foo1", "foo2": "$foo2", "sum_v": "$sum_v"},
"extract": { "headers": {"User-Agent": "HttpRunner/${get_httprunner_version()}"},
"session_foo2": "body.args.foo2" },
}, "extract": {"session_foo2": "body.args.foo2"},
"validate": [ "validate": [
{"eq": ["status_code", 200]}, {"eq": ["status_code", 200]},
{"eq": ["body.args.foo1", "session_bar1"]}, {"eq": ["body.args.foo1", "session_bar1"]},
{"eq": ["body.args.foo2", "session_bar2"]}, {"eq": ["body.args.sum_v", 3]},
{"eq": ["body.args.sum_v", 3]} {"eq": ["body.args.foo2", "session_bar2"]},
] ],
}), }
TestStep(**{ ),
"name": "post raw text", TestStep(
"variables": { **{
"foo1": "hello world", "name": "post raw text",
"foo3": "$session_foo2" "variables": {"foo1": "hello world", "foo3": "$session_foo2"},
}, "request": {
"request": { "method": "POST",
"method": "POST", "url": "/post",
"url": "/post", "headers": {
"data": "This is expected to be sent back as part of response body: $foo1-$foo3.", "User-Agent": "HttpRunner/${get_httprunner_version()}",
"headers": { "Content-Type": "text/plain",
"User-Agent": "HttpRunner/${get_httprunner_version()}", },
"Content-Type": "text/plain" "data": "This is expected to be sent back as part of response body: $foo1-$foo3.",
} },
}, "validate": [
"validate": [ {"eq": ["status_code", 200]},
{"eq": ["status_code", 200]}, {
{"eq": [ "eq": [
"body.data", "body.data",
"This is expected to be sent back as part of response body: session_bar1-session_bar2." "This is expected to be sent back as part of response body: session_bar1-session_bar2.",
]}, ]
] },
}), ],
TestStep(**{ }
"name": "post form data", ),
"variables": { TestStep(
"foo1": "session_bar1", **{
"foo2": "bar2" "name": "post form data",
}, "variables": {"foo1": "bar1", "foo2": "bar2"},
"request": { "request": {
"method": "POST", "method": "POST",
"url": "/post", "url": "/post",
"data": "foo1=$foo1&foo2=$foo2", "headers": {
"headers": { "User-Agent": "HttpRunner/${get_httprunner_version()}",
"User-Agent": "HttpRunner/${get_httprunner_version()}", "Content-Type": "application/x-www-form-urlencoded",
"Content-Type": "application/x-www-form-urlencoded" },
} "data": "foo1=$foo1&foo2=$foo2",
}, },
"validate": [ "validate": [
{"eq": ["status_code", 200]}, {"eq": ["status_code", 200]},
{"eq": ["body.form.foo1", "session_bar1"]}, {"eq": ["body.form.foo1", "session_bar1"]},
{"eq": ["body.form.foo2", "bar2"]} {"eq": ["body.form.foo2", "bar2"]},
] ],
}) }
),
] ]
def test_start(self):
if __name__ == '__main__': TestCaseRunner(self.config, self.teststeps).run()
TestCaseRequestMethodsWithFunctions().run()
@@ -1,34 +1,30 @@
from examples.postman_echo import debugtalk # NOTICE: Generated By HttpRunner. DO'NOT EDIT!
from examples.postman_echo.request_methods.validate_with_variables_test \ import unittest
import TestCaseRequestMethodsValidateWithVariables
from httprunner.runner import TestCaseRunner from httprunner.runner import TestCaseRunner
from httprunner.schema import TestsConfig, TestStep from httprunner.schema import TestsConfig, TestStep
class TestCaseRequestMethodsRefTestcase(TestCaseRunner): class TestCaseRequestWithTestcaseReference(unittest.TestCase):
config = TestsConfig(**{ config = TestsConfig(
"name": "request methods testcase: reference testcase", **{
"variables": { "name": "request methods testcase: reference testcase",
"foo1": "session_bar1" "variables": {"foo1": "session_bar1"},
}, "base_url": "https://postman-echo.com",
"functions": { "verify": False,
"get_httprunner_version": debugtalk.get_httprunner_version, "path": "examples/postman_echo/request_methods/request_with_testcase_reference_test.py",
"sum_two": debugtalk.sum_two }
}, )
"base_url": "https://postman-echo.com",
"verify": False
})
teststeps = [ teststeps = [
TestStep(**{ TestStep(
"name": "get with params", **{
"variables": { "name": "request with variables",
"foo1": "override_bar1" "variables": {"foo1": "override_bar1"},
}, "testcase": "request_methods/request_with_variables.yml",
"testcase": TestCaseRequestMethodsValidateWithVariables }
}) ),
] ]
def test_start(self):
if __name__ == '__main__': TestCaseRunner(self.config, self.teststeps).run()
TestCaseRequestMethodsRefTestcase().run()
@@ -1,14 +1,18 @@
# NOTICE: Generated By HttpRunner. DO'NOT EDIT!
import unittest
from httprunner.runner import TestCaseRunner from httprunner.runner import TestCaseRunner
from httprunner.schema import TestsConfig, TestStep from httprunner.schema import TestsConfig, TestStep
class TestCaseRequestWithVariables(TestCaseRunner): class TestCaseRequestWithVariables(unittest.TestCase):
config = TestsConfig( config = TestsConfig(
**{ **{
"name": "request methods testcase with variables", "name": "request methods testcase with variables",
"variables": {"foo1": "session_bar1"}, "variables": {"foo1": "session_bar1"},
"base_url": "https://postman-echo.com", "base_url": "https://postman-echo.com",
"verify": False, "verify": False,
"path": "examples/postman_echo/request_methods/request_with_variables_test.py",
} }
) )
@@ -77,6 +81,5 @@ class TestCaseRequestWithVariables(TestCaseRunner):
), ),
] ]
def test_start(self):
if __name__ == "__main__": TestCaseRunner(self.config, self.teststeps).run()
TestCaseRequestWithVariables().run()
@@ -1,53 +1,45 @@
# NOTICE: Generated By HttpRunner. DO'NOT EDIT!
import unittest
from httprunner.runner import TestCaseRunner from httprunner.runner import TestCaseRunner
from httprunner.schema import TestsConfig, TestStep from httprunner.schema import TestsConfig, TestStep
from examples.postman_echo import debugtalk
class TestCaseRequestMethodsValidateWithFunctions(TestCaseRunner): class TestCaseValidateWithFunctions(unittest.TestCase):
config = TestsConfig(**{ config = TestsConfig(
"name": "request methods testcase: validate with functions", **{
"variables": { "name": "request methods testcase: validate with functions",
"foo1": "session_bar1" "variables": {"foo1": "session_bar1"},
}, "base_url": "https://postman-echo.com",
"functions": { "verify": False,
"get_httprunner_version": debugtalk.get_httprunner_version, "path": "examples/postman_echo/request_methods/validate_with_functions_test.py",
"sum_two": debugtalk.sum_two }
}, )
"base_url": "https://postman-echo.com",
"verify": False
})
teststeps = [ teststeps = [
TestStep(**{ TestStep(
"name": "get with params", **{
"variables": { "name": "get with params",
"foo1": "bar1", "variables": {
"foo2": "session_bar2", "foo1": "bar1",
"sum_v": "${sum_two(1, 2)}" "foo2": "session_bar2",
}, "sum_v": "${sum_two(1, 2)}",
"request": {
"method": "GET",
"url": "/get",
"params": {
"foo1": "$foo1",
"foo2": "$foo2",
"sum_v": "$sum_v"
}, },
"headers": { "request": {
"User-Agent": "HttpRunner/${get_httprunner_version()}" "method": "GET",
} "url": "/get",
}, "params": {"foo1": "$foo1", "foo2": "$foo2", "sum_v": "$sum_v"},
"extract": { "headers": {"User-Agent": "HttpRunner/${get_httprunner_version()}"},
"session_foo2": "body.args.foo2" },
}, "extract": {"session_foo2": "body.args.foo2"},
"validate": [ "validate": [
{"eq": ["status_code", 200]}, {"eq": ["status_code", 200]},
{"eq": ["body.args.sum_v", 3]}, {"eq": ["body.args.sum_v", 3]},
{"less_than": ["body.args.sum_v", "${sum_two(2, 2)}"]} {"less_than": ["body.args.sum_v", "${sum_two(2, 2)}"]},
] ],
}) }
),
] ]
def test_start(self):
if __name__ == '__main__': TestCaseRunner(self.config, self.teststeps).run()
TestCaseRequestMethodsValidateWithFunctions().run()
@@ -1,99 +1,85 @@
# NOTICE: Generated By HttpRunner. DO'NOT EDIT!
import unittest
from httprunner.runner import TestCaseRunner from httprunner.runner import TestCaseRunner
from httprunner.schema import TestsConfig, TestStep from httprunner.schema import TestsConfig, TestStep
class TestCaseRequestMethodsValidateWithVariables(TestCaseRunner): class TestCaseValidateWithVariables(unittest.TestCase):
config = TestsConfig(**{ config = TestsConfig(
"name": "request methods testcase: validate with variables", **{
"variables": { "name": "request methods testcase: validate with variables",
"foo1": "session_bar1" "variables": {"foo1": "session_bar1"},
}, "base_url": "https://postman-echo.com",
"base_url": "https://postman-echo.com", "verify": False,
"verify": False "path": "examples/postman_echo/request_methods/validate_with_variables_test.py",
}) }
)
teststeps = [ teststeps = [
TestStep(**{ TestStep(
"name": "get with params", **{
"variables": { "name": "get with params",
"foo1": "bar1", "variables": {"foo1": "bar1", "foo2": "session_bar2"},
"foo2": "session_bar2" "request": {
}, "method": "GET",
"request": { "url": "/get",
"method": "GET", "params": {"foo1": "$foo1", "foo2": "$foo2"},
"url": "/get", "headers": {"User-Agent": "HttpRunner/3.0"},
"params": {
"foo1": "$foo1",
"foo2": "$foo2"
}, },
"headers": { "extract": {"session_foo2": "body.args.foo2"},
"User-Agent": "HttpRunner/3.0" "validate": [
} {"eq": ["status_code", 200]},
}, {"eq": ["body.args.foo1", "$foo1"]},
"extract": { {"eq": ["body.args.foo2", "$foo2"]},
"session_foo2": "body.args.foo2" ],
}, }
"validate": [ ),
{"eq": ["status_code", 200]}, TestStep(
{"eq": ["body.args.foo1", "session_bar1"]}, **{
{"eq": ["body.args.foo1", "$foo1"]}, "name": "post raw text",
{"eq": ["body.args.foo2", "session_bar2"]}, "variables": {"foo1": "hello world", "foo3": "$session_foo2"},
{"eq": ["body.args.foo2", "$foo2"]} "request": {
] "method": "POST",
}), "url": "/post",
TestStep(**{ "headers": {
"name": "post raw text", "User-Agent": "HttpRunner/3.0",
"variables": { "Content-Type": "text/plain",
"foo1": "hello world", },
"foo3": "$session_foo2" "data": "This is expected to be sent back as part of response body: $foo1-$foo3.",
}, },
"request": { "validate": [
"method": "POST", {"eq": ["status_code", 200]},
"url": "/post", {
"data": "This is expected to be sent back as part of response body: $foo1-$foo3.", "eq": [
"headers": { "body.data",
"User-Agent": "HttpRunner/3.0", "This is expected to be sent back as part of response body: session_bar1-$foo3.",
"Content-Type": "text/plain" ]
} },
}, ],
"validate": [ }
{"eq": ["status_code", 200]}, ),
{"eq": [ TestStep(
"body.data", **{
"This is expected to be sent back as part of response body: session_bar1-session_bar2." "name": "post form data",
]}, "variables": {"foo1": "bar1", "foo2": "bar2"},
{"eq": [ "request": {
"body.data", "method": "POST",
"This is expected to be sent back as part of response body: $foo1-$foo3." "url": "/post",
]}, "headers": {
] "User-Agent": "HttpRunner/3.0",
}), "Content-Type": "application/x-www-form-urlencoded",
TestStep(**{ },
"name": "post form data", "data": "foo1=$foo1&foo2=$foo2",
"variables": { },
"foo1": "session_bar1", "validate": [
"foo2": "bar2" {"eq": ["status_code", 200]},
}, {"eq": ["body.form.foo1", "$foo1"]},
"request": { {"eq": ["body.form.foo2", "$foo2"]},
"method": "POST", ],
"url": "/post", }
"data": "foo1=$foo1&foo2=$foo2", ),
"headers": {
"User-Agent": "HttpRunner/3.0",
"Content-Type": "application/x-www-form-urlencoded"
}
},
"validate": [
{"eq": ["status_code", 200]},
{"eq": ["body.form.foo1", "session_bar1"]},
{"eq": ["body.form.foo1", "$foo1"]},
{"eq": ["body.form.foo2", "bar2"]},
{"eq": ["body.form.foo2", "$foo2"]}
]
})
] ]
def test_start(self):
if __name__ == '__main__': TestCaseRunner(self.config, self.teststeps).run()
runner = TestCaseRequestMethodsValidateWithVariables().run()
print(runner.step_datas)
+1 -1
View File
@@ -75,7 +75,7 @@ class HttpRunner(object):
testcase.config.variables.update(project_meta.variables) testcase.config.variables.update(project_meta.variables)
testcase.config.functions.update(project_meta.functions) testcase.config.functions.update(project_meta.functions)
test_runner = TestCaseRunner().init(testcase) test_runner = TestCaseRunner(testcase.config, testcase.teststeps)
TestSequense = type("TestSequense", (unittest.TestCase,), {}) TestSequense = type("TestSequense", (unittest.TestCase,), {})
test_method = _add_test(test_runner) test_method = _add_test(test_runner)
+33 -11
View File
@@ -3,14 +3,17 @@ import os
import jinja2 import jinja2
from loguru import logger from loguru import logger
from httprunner.new_loader import load_testcase_file from httprunner import exceptions
from httprunner.new_loader import load_testcase_file, load_folder_files
__TMPL__ = """# NOTICE: Generated By HttpRunner. DO'NOT EDIT!
import unittest
__TMPL__ = """
from httprunner.runner import TestCaseRunner from httprunner.runner import TestCaseRunner
from httprunner.schema import TestsConfig, TestStep from httprunner.schema import TestsConfig, TestStep
class {{ class_name }}(TestCaseRunner): class {{ class_name }}(unittest.TestCase):
config = TestsConfig(**{{ config }}) config = TestsConfig(**{{ config }})
teststeps = [ teststeps = [
@@ -19,31 +22,50 @@ class {{ class_name }}(TestCaseRunner):
{% endfor %} {% endfor %}
] ]
def test_start(self):
TestCaseRunner(self.config, self.teststeps).run()
if __name__ == '__main__':
{{ class_name }}().run()
""" """
def make_testcase(path: str) -> str: def make_testcase(testcase_path: str) -> str:
testcase = load_testcase_file(path) testcase = load_testcase_file(testcase_path)
template = jinja2.Template(__TMPL__) template = jinja2.Template(__TMPL__)
raw_file_name, _ = os.path.splitext(os.path.basename(path)) raw_file_name, _ = os.path.splitext(os.path.basename(testcase_path))
# convert title case, e.g. request_with_variables => RequestWithVariables # convert title case, e.g. request_with_variables => RequestWithVariables
name_in_title_case = raw_file_name.title().replace("_", "") name_in_title_case = raw_file_name.title().replace("_", "")
testcase_dir = os.path.dirname(testcase_path)
testcase_python_path = os.path.join(testcase_dir, f"{raw_file_name}_test.py")
config = testcase["config"]
config["path"] = testcase_python_path
data = { data = {
"class_name": f"TestCase{name_in_title_case}", "class_name": f"TestCase{name_in_title_case}",
"config": testcase["config"], "config": config,
"teststeps": testcase["teststeps"], "teststeps": testcase["teststeps"],
} }
content = template.render(data) content = template.render(data)
testcase_dir = os.path.dirname(path)
testcase_python_path = os.path.join(testcase_dir, f"{raw_file_name}_test.py")
with open(testcase_python_path, "w") as f: with open(testcase_python_path, "w") as f:
f.write(content) f.write(content)
logger.info(f"generated testcase: {testcase_python_path}") logger.info(f"generated testcase: {testcase_python_path}")
return testcase_python_path return testcase_python_path
def make(tests_path: str) -> list:
testcases = []
if os.path.isdir(tests_path):
files_list = load_folder_files(tests_path)
testcases.extend(files_list)
elif os.path.isfile(tests_path):
testcases.append(tests_path)
else:
raise exceptions.TestcaseNotFound(f"Invalid tests path: {tests_path}")
return [
make_testcase(testcase_path)
for testcase_path in testcases
]
+21
View File
@@ -0,0 +1,21 @@
import unittest
from httprunner.make import make_testcase, make
class TestLoader(unittest.TestCase):
def test_make_testcase(self):
path = "examples/postman_echo/request_methods/request_with_variables.yml"
testcase_python_path = make_testcase(path)
self.assertEqual(
testcase_python_path,
"examples/postman_echo/request_methods/request_with_variables_test.py"
)
def test_make_testcase_folder(self):
path = "examples/postman_echo/request_methods/"
testcase_python_list = make(path)
self.assertIn(
"examples/postman_echo/request_methods/request_with_functions_test.py",
testcase_python_list
)
+210 -1
View File
@@ -1,12 +1,14 @@
import importlib
import io import io
import json import json
import os import os
import sys
import types import types
import yaml import yaml
from loguru import logger from loguru import logger
from httprunner import builtin from httprunner import builtin, utils
from httprunner import exceptions from httprunner import exceptions
from httprunner.schema import TestCase from httprunner.schema import TestCase
@@ -64,6 +66,47 @@ def load_testcase_file(testcase_file):
return testcase_content return testcase_content
def load_dot_env_file(dot_env_path):
""" load .env file.
Args:
dot_env_path (str): .env file path
Returns:
dict: environment variables mapping
{
"UserName": "debugtalk",
"Password": "123456",
"PROJECT_KEY": "ABCDEFGH"
}
Raises:
exceptions.FileFormatError: If .env file format is invalid.
"""
if not os.path.isfile(dot_env_path):
return {}
logger.info(f"Loading environment variables from {dot_env_path}")
env_variables_mapping = {}
with io.open(dot_env_path, "r", encoding="utf-8") as fp:
for line in fp:
# maxsplit=1
if "=" in line:
variable, value = line.split("=", 1)
elif ":" in line:
variable, value = line.split(":", 1)
else:
raise exceptions.FileFormatError(".env format error")
env_variables_mapping[variable.strip()] = value.strip()
utils.set_os_environ(env_variables_mapping)
return env_variables_mapping
def load_folder_files(folder_path, recursive=True): def load_folder_files(folder_path, recursive=True):
""" load folder path, return all files endswith yml/yaml/json in list. """ load folder path, return all files endswith yml/yaml/json in list.
@@ -133,3 +176,169 @@ def load_builtin_functions():
""" load builtin module functions """ load builtin module functions
""" """
return load_module_functions(builtin) return load_module_functions(builtin)
def locate_file(start_path, file_name):
""" locate filename and return absolute file path.
searching will be recursive upward until current working directory or system root dir.
Args:
file_name (str): target locate file name
start_path (str): start locating path, maybe file path or directory path
Returns:
str: located file path. None if file not found.
Raises:
exceptions.FileNotFound: If failed to locate file.
"""
if os.path.isfile(start_path):
start_dir_path = os.path.dirname(start_path)
elif os.path.isdir(start_path):
start_dir_path = start_path
else:
raise exceptions.FileNotFound(f"invalid path: {start_path}")
file_path = os.path.join(start_dir_path, file_name)
if os.path.isfile(file_path):
return os.path.abspath(file_path)
# current working directory
if os.path.abspath(start_dir_path) == os.getcwd():
raise exceptions.FileNotFound(f"{file_name} not found in {start_path}")
# system root dir
# Windows, e.g. 'E:\\'
# Linux/Darwin, '/'
parent_dir = os.path.dirname(start_dir_path)
if parent_dir == start_dir_path:
raise exceptions.FileNotFound(f"{file_name} not found in {start_path}")
# locate recursive upward
return locate_file(parent_dir, file_name)
def locate_debugtalk_py(start_path):
""" locate debugtalk.py file
Args:
start_path (str): start locating path,
maybe testcase file path or directory path
Returns:
str: debugtalk.py file path, None if not found
"""
try:
# locate debugtalk.py file.
debugtalk_path = locate_file(start_path, "debugtalk.py")
except exceptions.FileNotFound:
debugtalk_path = None
return debugtalk_path
def init_project_working_directory(test_path):
""" this should be called at startup
run test file:
run_path -> load_cases -> load_project_data -> init_project_working_directory
or run passed in data structure:
run -> init_project_working_directory
Args:
test_path: specified testfile path
Returns:
(str, str): debugtalk.py path, project_working_directory
"""
def prepare_path(path):
if not os.path.exists(path):
err_msg = f"path not exist: {path}"
logger.error(err_msg)
raise exceptions.FileNotFound(err_msg)
if not os.path.isabs(path):
path = os.path.join(os.getcwd(), path)
return path
test_path = prepare_path(test_path)
# locate debugtalk.py file
debugtalk_path = locate_debugtalk_py(test_path)
global project_working_directory
if debugtalk_path:
# The folder contains debugtalk.py will be treated as PWD.
project_working_directory = os.path.dirname(debugtalk_path)
else:
# debugtalk.py not found, use os.getcwd() as PWD.
project_working_directory = os.getcwd()
# add PWD to sys.path
sys.path.insert(0, project_working_directory)
return debugtalk_path, project_working_directory
def load_debugtalk_functions():
""" load project debugtalk.py module functions
debugtalk.py should be located in project working directory.
Returns:
dict: debugtalk module functions mapping
{
"func1_name": func1,
"func2_name": func2
}
"""
# load debugtalk.py module
imported_module = importlib.import_module("debugtalk")
return load_module_functions(imported_module)
def load_project_data(test_path: str, dot_env_path: str = None):
""" load api, testcases, .env, debugtalk.py functions.
api/testcases folder is relative to project_working_directory
Args:
test_path (str): test file/folder path, locate pwd from this path.
dot_env_path (str): specified .env file path
Returns:
dict: project loaded api/testcases definitions,
environments and debugtalk.py functions.
"""
debugtalk_path, project_working_directory = init_project_working_directory(
test_path
)
project_meta = {}
# load .env file
# NOTICE:
# environment variable maybe loaded in debugtalk.py
# thus .env file should be loaded before loading debugtalk.py
dot_env_path = dot_env_path or os.path.join(project_working_directory, ".env")
project_meta["env"] = load_dot_env_file(dot_env_path)
if debugtalk_path:
# load debugtalk.py functions
debugtalk_functions = load_debugtalk_functions()
else:
debugtalk_functions = {}
# locate PWD and load debugtalk.py functions
project_meta["PWD"] = project_working_directory
project_meta["functions"] = debugtalk_functions
project_meta["test_path"] = os.path.abspath(test_path)[
len(project_working_directory) + 1 :
]
return project_meta
+17 -23
View File
@@ -2,38 +2,30 @@ from typing import List, Dict
from loguru import logger from loguru import logger
from httprunner import utils from httprunner import utils, exceptions
from httprunner.client import HttpSession from httprunner.client import HttpSession
from httprunner.exceptions import ValidationFailure, ParamsError from httprunner.exceptions import ValidationFailure, ParamsError
from httprunner.new_loader import load_project_data
from httprunner.parser import build_url, parse_data, parse_variables_mapping from httprunner.parser import build_url, parse_data, parse_variables_mapping
from httprunner.response import ResponseObject from httprunner.response import ResponseObject
from httprunner.schema import ( from httprunner.schema import (
TestsConfig, TestsConfig,
TestStep, TestStep,
VariablesMapping, VariablesMapping,
TestCase,
StepData, StepData,
) )
class TestCaseRunner(object): class TestCaseRunner(object):
config: TestsConfig = {} def __init__(self, config: TestsConfig, teststeps: List[TestStep], session: HttpSession = None):
teststeps: List[TestStep] = [] self.config = config
session: HttpSession = None self.teststeps = teststeps
step_datas: List[StepData] = [] self.session = session
validation_results: Dict = {} self.step_datas: List[StepData] = []
session_variables: Dict = {} self.validation_results: Dict = {}
success: bool = True # indicate testcase execution result self.session_variables: Dict = {}
self.success: bool = True # indicate testcase execution result
def init(self, testcase: TestCase) -> "TestCaseRunner":
self.config = testcase.config
self.teststeps = testcase.teststeps
return self
def with_session(self, s: HttpSession) -> "TestCaseRunner":
self.session = s
return self
def with_variables(self, **variables: VariablesMapping) -> "TestCaseRunner": def with_variables(self, **variables: VariablesMapping) -> "TestCaseRunner":
self.config.variables.update(variables) self.config.variables.update(variables)
@@ -142,8 +134,14 @@ class TestCaseRunner(object):
self.step_datas.append(step_data) self.step_datas.append(step_data)
return step_data.export return step_data.export
def test_start(self): def run(self):
"""main entrance""" """main entrance"""
if not self.config.path:
raise exceptions.ParamsError("config path missed!")
project_data = load_project_data(self.config.path)
self.config.functions = project_data["functions"]
self.step_datas.clear() self.step_datas.clear()
self.session_variables.clear() self.session_variables.clear()
for step in self.teststeps: for step in self.teststeps:
@@ -162,10 +160,6 @@ class TestCaseRunner(object):
return self return self
def run(self):
"""main entrance alias for test_start"""
return self.test_start()
def get_export_variables(self): def get_export_variables(self):
export_vars_mapping = {} export_vars_mapping = {}
for var_name in self.config.export: for var_name in self.config.export:
+1
View File
@@ -40,6 +40,7 @@ class TestsConfig(BaseModel):
setup_hooks: Hook = [] setup_hooks: Hook = []
teardown_hooks: Hook = [] teardown_hooks: Hook = []
export: Export = [] export: Export = []
path: Text = None
class Request(BaseModel): class Request(BaseModel):