Skip to content

Commit 09df1ed

Browse files
authored
Make reporters to be used independently (#28)
* show traceback verbosity on --debug-testipy * replace dict by TestMethodAttr * switch order for attr and name * rename reporter to junit * remove tests
1 parent 0b26020 commit 09df1ed

34 files changed

Lines changed: 901 additions & 1777 deletions

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -146,12 +146,12 @@ class SuitePetStore:
146146
self.toolbox = Toolbox()
147147
148148
# Create a new pet
149-
def test_create_pet_valid(self, ma: Dict, rm: ReportManager, ncycles=1, param=None):
149+
def test_create_pet_valid(self, sd: SuiteDetails, rm: ReportManager, ncycles=1, param=None):
150150
"""
151151
@LEVEL 3
152152
@PRIO 5
153153
"""
154-
current_test = rm.startTest(ma)
154+
current_test = rm.startTest(sd)
155155
156156
data = {
157157
"control": {"expected_status_code": 200},
@@ -167,13 +167,13 @@ class SuitePetStore:
167167
rm.testPassed(current_test, reason_of_state="pet created")
168168
169169
# Get the pet created before
170-
def test_get_pet_valid(self, ma: Dict, rm: ReportManager, ncycles=1, param=None):
170+
def test_get_pet_valid(self, sd: SuiteDetails, rm: ReportManager, ncycles=1, param=None):
171171
"""
172172
@LEVEL 3
173173
@PRIO 10
174174
@ON_SUCCESS 5
175175
"""
176-
current_test = rm.startTest(ma)
176+
current_test = rm.startTest(sd)
177177
178178
data = {
179179
"control": {"expected_status_code": 200},

testipy/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import logging
44

55

6-
__version__ = "0.9.6"
6+
__version__ = "0.10.1"
77
__app__ = "TestiPy"
88
__app_full__ = "Python Test Tool"
99
__author__ = "Pedro Nunes"

testipy/configs/enums_data.py

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
1-
TAG_NAME = "@NAME"
2-
TAG_TAG = "@TAG"
3-
TAG_LEVEL = "@LEVEL"
4-
TAG_PRIO = "@PRIO"
5-
TAG_FEATURES = "@FEATURES"
6-
TAG_TESTNUMBER = "@TN"
7-
TAG_DEPENDS = "@DEPENDS"
8-
TAG_ON_SUCCESS = "@ON_SUCCESS"
9-
TAG_ON_FAILURE = "@ON_FAILURE"
1+
PREFIX_TAGS = "@"
2+
3+
TAG_NAME = PREFIX_TAGS + "NAME"
4+
TAG_TAG = PREFIX_TAGS + "TAG"
5+
TAG_LEVEL = PREFIX_TAGS + "LEVEL"
6+
TAG_PRIO = PREFIX_TAGS + "PRIO"
7+
TAG_FEATURES = PREFIX_TAGS + "FEATURES"
8+
TAG_TESTNUMBER = PREFIX_TAGS + "TN"
9+
TAG_DEPENDS = PREFIX_TAGS + "DEPENDS"
10+
TAG_ON_SUCCESS = PREFIX_TAGS + "ON_SUCCESS"
11+
TAG_ON_FAILURE = PREFIX_TAGS + "ON_FAILURE"
1012

1113
STATE_PASSED = "PASS"
1214
STATE_SKIPPED = "SKIP"

testipy/engine/execute_tests.py

Lines changed: 85 additions & 86 deletions
Large diffs are not rendered by default.

testipy/engine/execution_logger.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
import sys
33
import logging
44
import logging.config
5+
import traceback
6+
57
import yaml
68

79
from testipy import get_exec_logger
@@ -21,10 +23,11 @@ def setup_logging(log_filename: str) -> logging.Logger:
2123

2224
class ExecutionLogger:
2325

24-
def __init__(self, results_folder_runtime: str):
26+
def __init__(self, results_folder_runtime: str, verbose: bool = False) -> None:
2527
self._log_file_full_path = os.path.join(results_folder_runtime, execution_log_filename)
2628
self._create_results_folder(results_folder_runtime)
2729
self._logger = setup_logging(self._log_file_full_path)
30+
self._verbose = verbose
2831

2932
def _create_results_folder(self, folder_name):
3033
try:
@@ -42,6 +45,10 @@ def __enter__(self):
4245
def __exit__(self, exc_type, exc_val, exc_tb):
4346
if exc_val:
4447
self._logger.error(str(exc_val), exc_info=True)
48+
if self._verbose:
49+
print(traceback.format_exc())
50+
self._logger.error(traceback.format_exc(), exc_info=True)
51+
4552
self.close_logger()
4653
return self
4754

testipy/engine/models.py

Lines changed: 258 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,258 @@
1+
from typing import Union, List, Set, Dict
2+
3+
4+
class TestMethodAttr:
5+
def __init__(
6+
self, suite_attr: "SuiteAttr", method_name: str,
7+
method_obj: object = None, ncycles: int = 1, param: object = None,
8+
name: str = "", comment: str = "", prio: int = 0, level: int = 1,
9+
features: str = "", test_number: str = "",
10+
tags: Set[str] = None,
11+
depends: Set[int] = None, on_success: Set[int] = None, on_failure: Set[int] = None
12+
):
13+
self.suite_attr: SuiteAttr = suite_attr
14+
15+
self.method_name: str = method_name
16+
self.method_obj: object = method_obj
17+
self.method_id: int = suite_attr.get_max_test_method_id()
18+
19+
self.param: object = param
20+
self.ncycles: int = ncycles
21+
22+
self.name: str = name or method_name
23+
self.comment: str = comment
24+
self.prio: int = prio
25+
self.level: int = level
26+
self.features: str = features
27+
self.test_number: str = test_number
28+
self.tags: Set[str] = tags or set()
29+
30+
self.depends: Set[int] = depends or set()
31+
self.on_success: Set[int] = on_success or set()
32+
self.on_failure: Set[int] = on_failure or set()
33+
34+
suite_attr.test_method_attr_list.append(self)
35+
36+
def duplicate(self, suite_attr: "SuiteAttr"):
37+
_new_attr = TestMethodAttr(
38+
suite_attr=suite_attr,
39+
40+
method_name=self.method_name,
41+
method_obj=self.method_obj,
42+
43+
param=self.param,
44+
ncycles=self.ncycles,
45+
46+
name=self.name,
47+
comment=self.comment,
48+
prio=self.prio,
49+
level=self.level,
50+
features=self.features,
51+
test_number=self.test_number,
52+
tags = self.tags,
53+
54+
depends=self.depends,
55+
on_success=self.on_success,
56+
on_failure=self.on_failure
57+
)
58+
_new_attr.method_id = self.method_id
59+
60+
return _new_attr
61+
62+
def get_common_attr_as_dict(self) -> Dict:
63+
return dict(
64+
method_name=self.method_name,
65+
method_id=self.method_id,
66+
67+
param=self.param,
68+
ncycles=self.ncycles,
69+
70+
name=self.name,
71+
comment=self.comment,
72+
prio=self.prio,
73+
level=self.level,
74+
features=self.features,
75+
test_number=self.test_number,
76+
tags=list(self.tags),
77+
78+
depends=list(self.depends),
79+
on_success=list(self.on_success),
80+
on_failure=list(self.on_failure)
81+
)
82+
83+
def __repr__(self):
84+
return f"<TestMethodAttr {self.method_id}: {self.method_name}[x{self.ncycles}]>"
85+
86+
87+
class SuiteAttr:
88+
def __init__(self, package_attr: "PackageAttr", filename: str, suite_name: str,
89+
suite_obj: object = None, ncycles: int = 1, suite_kwargs: dict = None,
90+
test_method_attr_list: List[TestMethodAttr] = None,
91+
name: str = "", comment: str = "", prio: int = 0, level: int = 1,
92+
features: str = "", test_number: str = "",
93+
tags: Set[str] = None):
94+
self.package: PackageAttr = package_attr
95+
96+
self.filename: str = filename
97+
self.suite_name: str = suite_name
98+
self.suite_obj = suite_obj
99+
self.suite_id: int = package_attr.get_max_suite_id()
100+
self.suite_kwargs: dict = suite_kwargs or {}
101+
102+
self.app = None
103+
104+
self.test_method_attr_list: List[TestMethodAttr] = test_method_attr_list or []
105+
self.ncycles: int = ncycles
106+
107+
self.name: str = name or filename
108+
self.comment: str = comment
109+
self.prio: int = prio
110+
self.level: int = level
111+
self.features: str = features
112+
self.test_number: str = test_number
113+
self.tags: Set[str] = tags or set()
114+
115+
package_attr.suite_attr_list.append(self)
116+
117+
def get_test_method_by_name(self, name: str) -> Union[TestMethodAttr, None]:
118+
for test_method_attr in self.test_method_attr_list:
119+
if test_method_attr.method_name == name or test_method_attr.name == name:
120+
return test_method_attr
121+
return None
122+
123+
def get_max_test_method_id(self) -> int:
124+
_max_rec = len(self.test_method_attr_list)
125+
_max_id = max([ma.method_id for ma in self.test_method_attr_list]) if _max_rec > 0 else 0
126+
return max(_max_rec, _max_id) + 1
127+
128+
def duplicate(self, package: "PackageAttr", clone_children: bool = True):
129+
_new_attr = SuiteAttr(
130+
package_attr=package,
131+
132+
filename=self.filename,
133+
suite_name=self.suite_name,
134+
suite_obj=self.suite_obj,
135+
suite_kwargs=self.suite_kwargs,
136+
137+
test_method_attr_list=self.test_method_attr_list,
138+
ncycles=self.ncycles,
139+
140+
name=self.name,
141+
comment=self.comment,
142+
prio=self.prio,
143+
level=self.level,
144+
tags=self.tags,
145+
features=self.features,
146+
test_number=self.test_number
147+
)
148+
_new_attr.suite_id = self.suite_id
149+
150+
if clone_children:
151+
for test_method_attr in self.test_method_attr_list:
152+
test_method_attr.duplicate(_new_attr)
153+
154+
return _new_attr
155+
156+
def __repr__(self):
157+
return f"<SuiteAttr {self.suite_id}: {self.suite_name}[x{self.ncycles}], Methods: {len(self.test_method_attr_list)}>"
158+
159+
160+
class PackageAttr:
161+
__counter: int = 0
162+
163+
def __init__(
164+
self,
165+
package_name: str,
166+
suite_attr_list: List[SuiteAttr] = None,
167+
ncycles: int = 1
168+
):
169+
self.__counter += 1
170+
self.package_name: str = package_name
171+
self.package_id: int = self.__counter
172+
173+
self.suite_attr_list: List[SuiteAttr] = suite_attr_list or []
174+
self.ncycles: int = ncycles
175+
176+
def get_suite_by_name(self, name: str) -> Union[SuiteAttr, None]:
177+
for suite in self.suite_attr_list:
178+
if suite.suite_name == name or suite.name == name:
179+
return suite
180+
return None
181+
182+
def get_max_suite_id(self) -> int:
183+
_max_rec = len(self.suite_attr_list)
184+
_max_id = max([suite_attr.suite_id for suite_attr in self.suite_attr_list]) if _max_rec > 0 else 0
185+
return max(_max_rec, _max_id) + 1
186+
187+
def duplicate(self, clone_children: bool = True):
188+
_new_attr = PackageAttr(
189+
package_name=self.package_name,
190+
suite_attr_list=self.suite_attr_list,
191+
ncycles=self.ncycles
192+
)
193+
_new_attr.package_id = self.package_id
194+
195+
if clone_children:
196+
for suite in self.suite_attr_list:
197+
suite.duplicate(_new_attr, clone_children=clone_children)
198+
199+
return _new_attr
200+
201+
def __repr__(self):
202+
return f"<PackageAttr {self.package_id}: {self.package_name}[x{self.ncycles}], Suites: {len(self.suite_attr_list)}>"
203+
204+
205+
def mark_packages_suites_methods_ids(package_attr_list: List[PackageAttr]) -> List[PackageAttr]:
206+
package_id = suite_id = method_id = 0
207+
208+
for package in package_attr_list:
209+
package_id += 1
210+
package.package_id = package_id
211+
212+
for suite in package.suite_attr_list:
213+
suite_id += 1
214+
suite.suite_id = suite_id
215+
216+
for method in suite.test_method_attr_list:
217+
method_id += 1
218+
method.method_id = method_id
219+
220+
return package_attr_list
221+
222+
223+
# Sorting function for package, suite, and method structure
224+
def sort_test_structure(package_attr_list: List[PackageAttr]) -> List[PackageAttr]:
225+
# Sort Packages
226+
package_attr_list.sort(key=lambda package: (package.package_name))
227+
228+
for package_attr in package_attr_list:
229+
# Sort Suites within each Package
230+
package_attr.suite_attr_list.sort(key=lambda suite: (suite.prio, suite.suite_name))
231+
232+
for suite_attr in package_attr.suite_attr_list:
233+
# Sort Test Methods within each Suite
234+
suite_attr.test_method_attr_list.sort(key=lambda test: (test.prio, test.method_name))
235+
236+
return package_attr_list
237+
238+
239+
def get_package_by_name(package_attr_list: List[PackageAttr], name: str) -> Union[PackageAttr, None]:
240+
for package_attr in package_attr_list:
241+
if package_attr.package_name == name:
242+
return package_attr
243+
return None
244+
245+
246+
# show test list
247+
def show_test_structure(package_attr_list: List[PackageAttr]) -> str:
248+
str_res = ""
249+
for package_attr in package_attr_list:
250+
str_res += f"\n{package_attr}\n"
251+
252+
for suite_attr in package_attr.suite_attr_list:
253+
str_res += f"\t{suite_attr}\n"
254+
255+
for test_method_attr in suite_attr.test_method_attr_list:
256+
str_res += f"\t\t{test_method_attr}\n"
257+
258+
return str_res[1:-1]

0 commit comments

Comments
 (0)