Skip to content

Commit ff60ef5

Browse files
committed
Add test cases to test method extraction.
Add test_py2puml_with_methods to test py2puml on class containing methods. Refactor test_py2puml_with_subdomain by moving expected result to file.
1 parent ad7712c commit ff60ef5

File tree

6 files changed

+123
-22
lines changed

6 files changed

+123
-22
lines changed

tests/asserts/method.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
from py2puml.domain.umlclass import UmlMethod
2+
3+
4+
def assert_method(method: UmlMethod, expected_name: str, expected_signature: str):
5+
assert method.name == expected_name
6+
assert method.signature == expected_signature
7+
#TODO: add 'is_static' attribute to UmlMethod for static methods
8+
#TODO: add 'is_class' attribute to UmlMethod for class methods

tests/data/with_methods.puml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
@startuml
2+
class tests.modules.withmethods.withmethods.Coordinates {
3+
x: float
4+
y: float
5+
}
6+
class tests.modules.withmethods.withmethods.Point {
7+
PI: float {static}
8+
coordinates: Coordinates
9+
day_unit: TimeUnit
10+
hour_unit: TimeUnit
11+
time_resolution: Tuple[str, TimeUnit]
12+
x: int
13+
y: str
14+
void __init__(x: int, y: str, unit: str, u: float, z: List[int])
15+
(float, float) get_coordinates()
16+
{static} Point from_values(x: int, y: str, u: float, z: List[int])
17+
}
18+
tests.modules.withmethods.withmethods.Point *-- tests.modules.withmethods.withmethods.Coordinates
19+
@enduml

tests/data/with_subdomain.puml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
@startuml
2+
class tests.modules.withsubdomain.subdomain.insubdomain.Engine {
3+
horsepower: int
4+
}
5+
class tests.modules.withsubdomain.subdomain.insubdomain.Pilot {
6+
name: str
7+
}
8+
class tests.modules.withsubdomain.withsubdomain.Car {
9+
name: str
10+
engine: Engine
11+
}
12+
tests.modules.withsubdomain.withsubdomain.Car *-- tests.modules.withsubdomain.subdomain.insubdomain.Engine
13+
@enduml
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
from typing import List, Tuple
2+
from math import pi
3+
4+
from tests.modules import withenum
5+
from tests import modules
6+
from tests.modules.withenum import TimeUnit
7+
8+
9+
class Coordinates:
10+
def __init__(self, x: float, y: float):
11+
self.x = x
12+
self.y = y
13+
14+
15+
class Point:
16+
PI: float = pi
17+
origin = Coordinates(0, 0)
18+
19+
@staticmethod
20+
def from_values(x: int, y: str, u: float, z: List[int]) -> 'Point':
21+
return Point(x, y, u, z)
22+
23+
def get_coordinates(self):
24+
return self.x, self.y
25+
26+
def __init__(self, x: int, y: str, unit: str, u: float, z: List[int]):
27+
self.coordinates: Coordinates = Coordinates(x, float(y))
28+
# al the different imports of TimeUnit must be handled and result in the same 'short type' to display
29+
self.day_unit: withenum.TimeUnit = withenum.TimeUnit.DAYS
30+
self.hour_unit: modules.withenum.TimeUnit = modules.withenum.TimeUnit.HOURS
31+
self.time_resolution: Tuple[str, withenum.TimeUnit] = 'minute', TimeUnit.MINUTE
32+
self.x = x
33+
self.y = y

tests/py2puml/inspection/test_inspectclass.py

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,10 @@
66
from py2puml.domain.umlclass import UmlClass, UmlAttribute
77
from py2puml.domain.umlrelation import UmlRelation, RelType
88
from py2puml.inspection.inspectmodule import inspect_module
9-
from py2puml.parsing.astvisitors import ConstructorVisitor
10-
from py2puml.parsing.moduleresolver import ModuleResolver
11-
from py2puml.parsing.parseclassconstructor import parse_class_constructor
129

1310
from tests.asserts.attribute import assert_attribute
1411
from tests.asserts.relation import assert_relation
12+
from tests.asserts.method import assert_method
1513

1614

1715
def test_inspect_module_should_find_static_and_instance_attributes():
@@ -70,6 +68,7 @@ def test_inspect_module_should_find_static_and_instance_attributes():
7068
RelType.COMPOSITION
7169
)
7270

71+
7372
def test_inspect_module_should_find_abstract_class():
7473
domain_items_by_fqn: Dict[str, UmlItem] = {}
7574
domain_relations: List[UmlRelation] = []
@@ -91,6 +90,7 @@ def test_inspect_module_should_find_abstract_class():
9190
assert domain_relations[0].source_fqn == 'tests.modules.withabstract.ClassTemplate'
9291
assert domain_relations[0].target_fqn == 'tests.modules.withabstract.ConcreteClass'
9392

93+
9494
def test_inspect_module_parse_class_constructor_should_not_process_inherited_constructor():
9595
domain_items_by_fqn: Dict[str, UmlItem] = {}
9696
domain_relations: List[UmlRelation] = []
@@ -127,6 +127,7 @@ def test_inspect_module_parse_class_constructor_should_not_process_inherited_con
127127
unit_attribute = metric_origin_umlitem.attributes[0]
128128
assert_attribute(unit_attribute, 'unit', 'str', expected_staticity=True)
129129

130+
130131
def test_inspect_module_should_unwrap_decorated_constructor():
131132
domain_items_by_fqn: Dict[str, UmlItem] = {}
132133
domain_relations: List[UmlRelation] = []
@@ -148,3 +149,33 @@ def test_inspect_module_should_unwrap_decorated_constructor():
148149
# PointDecoratedWithoutWrapping UmlClass
149150
point_without_wrapping_umlitem: UmlClass = domain_items_by_fqn['tests.modules.withwrappedconstructor.PointDecoratedWithoutWrapping']
150151
assert len(point_without_wrapping_umlitem.attributes) == 0, 'the attributes of the original constructor could not be found, the constructor was not wrapped by the decorator'
152+
153+
154+
def test_inspect_module_should_find_methods():
155+
""" Test that methods are detected including static methods
156+
157+
This test case assumes that the methods will be sorted by type as follow:
158+
1a - instance methods (special methods aka "dunder")
159+
1b - all other instance methods
160+
2 - static methods """
161+
162+
domain_items_by_fqn: Dict[str, UmlItem] = {}
163+
domain_relations: List[UmlRelation] = []
164+
inspect_module(
165+
import_module('tests.modules.withmethods.withmethods'),
166+
'tests.modules.withmethods.withmethods',
167+
domain_items_by_fqn, domain_relations
168+
)
169+
170+
# Coordinates UmlClass
171+
coordinates_umlitem: UmlClass = domain_items_by_fqn['tests.modules.withmethods.withmethods.Coordinates']
172+
assert len(coordinates_umlitem.methods) == 0
173+
174+
# Point UmlClass
175+
point_umlitem: UmlClass = domain_items_by_fqn['tests.modules.withmethods.withmethods.Point']
176+
assert len(point_umlitem.methods) == 3
177+
178+
assert point_umlitem.methods[0].name == '__init__' # 1a - Instance method (special)
179+
assert point_umlitem.methods[1].name == 'get_coordinates' # 1b - Instance method (regular)
180+
assert point_umlitem.methods[2].name == 'from_values' # 2 - Static method
181+
# FIXME: use 'assert_method' once UmlMethod restructured

tests/py2puml/test_py2puml.py

Lines changed: 16 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
21
from pathlib import Path
32

4-
from py2puml.py2puml import py2puml
3+
from py2puml import py2puml
54

65
CURRENT_DIR = Path(__file__).parent
7-
from py2puml import py2puml
6+
DATA_DIR = CURRENT_DIR.parent / 'data'
87

98

109
def test_py2puml_model_on_py2uml_domain():
@@ -15,26 +14,24 @@ def test_py2puml_model_on_py2uml_domain():
1514
puml_content = list(py2puml.py2puml('py2puml/domain', 'py2puml.domain'))
1615
for line_index, (actual_line, expected_line) in enumerate(zip(puml_content, expected_puml_file)):
1716
assert actual_line == expected_line, f'updated and versionned content {domain_diagram_file_path} in line {line_index} has changed'
18-
17+
1918
assert line_index + 1 == len(puml_content), f'actual and expected diagrams have {line_index + 1} lines'
2019

2120

2221
def test_py2puml_with_subdomain():
23-
expected = """@startuml
24-
class tests.modules.withsubdomain.subdomain.insubdomain.Engine {
25-
horsepower: int
26-
}
27-
class tests.modules.withsubdomain.subdomain.insubdomain.Pilot {
28-
name: str
29-
}
30-
class tests.modules.withsubdomain.withsubdomain.Car {
31-
name: str
32-
engine: Engine
33-
}
34-
tests.modules.withsubdomain.withsubdomain.Car *-- tests.modules.withsubdomain.subdomain.insubdomain.Engine
35-
@enduml
36-
"""
22+
with open(DATA_DIR / 'with_subdomain.puml', 'r', encoding='utf-8') as fref:
23+
expected_data = fref.read()
3724
puml_content = py2puml.py2puml(
3825
'tests/modules/withsubdomain/', 'tests.modules.withsubdomain'
3926
)
40-
assert ''.join(puml_content) == expected
27+
actual_data = ''.join(puml_content)
28+
assert actual_data == expected_data
29+
30+
31+
def test_py2puml_with_methods():
32+
""" Test py2puml with a class containing methods """
33+
with open(DATA_DIR / 'with_methods.puml', 'r', encoding='utf-8') as fref:
34+
expected_data = fref.read()
35+
puml_content = py2puml.py2puml('tests/modules/withmethods', 'tests.modules.withmethods')
36+
actual_data = ''.join(puml_content)
37+
assert actual_data == expected_data

0 commit comments

Comments
 (0)