Skip to content

Commit 23f2c81

Browse files
committed
[tests] Introduce pytest fixtures for database management
Move database creation from class body to pytest fixtures. Eliminates file pollution and improves test isolation.
1 parent dfc2cad commit 23f2c81

3 files changed

Lines changed: 341 additions & 273 deletions

File tree

tests/conftest.py

Lines changed: 277 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,277 @@
1+
"""
2+
Pytest configuration and shared fixtures for WeatherRoutingTool tests.
3+
4+
This module provides reusable fixtures for test setup and teardown,
5+
replacing ad-hoc helper patterns with proper pytest fixtures.
6+
"""
7+
8+
import pytest
9+
import geopandas as gpd
10+
import sqlalchemy as db
11+
from shapely.geometry import LineString, Point, box
12+
13+
14+
@pytest.fixture(scope="class")
15+
def test_nodes_gdf():
16+
"""
17+
Fixture providing test nodes GeoDataFrame for seamark/constraint tests.
18+
19+
:return: GeoDataFrame with test node data
20+
:rtype: gpd.GeoDataFrame
21+
"""
22+
return gpd.GeoDataFrame(
23+
columns=["tags", "geometry"],
24+
data=[
25+
[{"waterway": "lock_gate", "seamark:type": "gate"}, Point(5, 15)],
26+
[{"seamark:type": "harbour"}, Point(9.91950, 57.06081)],
27+
[{"seamark:type": "buoy_cardinal"}, Point(12.01631, 48.92595)],
28+
[{"seamark:type": "separation_boundary"}, Point(12.01631, 48.92595)],
29+
[{"seamark:type": "separation_crossing"}, Point(12.01631, 48.92595)],
30+
[{"seamark:type": "separation_lane"}, Point(12.01631, 48.92595)],
31+
[{"seamark:type": "separation_roundabout"}, Point(12.01631, 48.92595)],
32+
[{"seamark:type": "separation_zone"}, Point(12.01631, 48.92595)],
33+
[{"seamark:type": "restricted_area"}, Point(12.01631, 48.92595)],
34+
],
35+
)
36+
37+
38+
@pytest.fixture(scope="class")
39+
def test_ways_gdf():
40+
"""
41+
Fixture providing test ways GeoDataFrame for seamark/constraint tests.
42+
43+
:return: GeoDataFrame with test ways data
44+
:rtype: gpd.GeoDataFrame
45+
"""
46+
return gpd.GeoDataFrame(
47+
columns=["tags", "geometry"],
48+
data=[
49+
[
50+
{"seamark:type": "separation_boundary"},
51+
LineString([(3.2738333, 51.8765), (3.154833, 51.853667)]),
52+
],
53+
[
54+
{"seamark:type": "separation_crossing"},
55+
LineString(
56+
[
57+
(6.3732417, 54.192091),
58+
(6.3593333, 54.1919199),
59+
(6.3310833, 54.1905871),
60+
(6.3182992, 54.189601),
61+
]
62+
),
63+
],
64+
[
65+
{"seamark:type": "separation_lane"},
66+
LineString([(24.6575999, 59.6085725), (24.7026512, 59.5505585)]),
67+
],
68+
[
69+
{"seamark:type": "separation_roundabout"},
70+
LineString(
71+
[
72+
(27.9974563, 43.186327),
73+
(27.998524, 43.1864565),
74+
(27.9995173, 43.186412),
75+
(28.0012373, 43.1859232),
76+
(28.0020059, 43.1854689),
77+
(28.0025203, 43.1850186),
78+
(28.0029253, 43.1845006),
79+
(28.0032216, 43.1838693),
80+
(27.9947856, 43.1813859),
81+
(27.9944414, 43.1819034),
82+
(27.9941705, 43.1826993),
83+
(27.9941723, 43.1835194),
84+
(27.9944142, 43.1842511),
85+
(27.9947709, 43.1848037),
86+
(27.9953623, 43.1853841),
87+
(27.9961109, 43.1858589),
88+
(27.9974563, 43.186327),
89+
]
90+
),
91+
],
92+
[
93+
{"seamark:type": "separation_zone"},
94+
LineString(
95+
[
96+
(-1.9830398, 49.2927514),
97+
(-1.9830233, 49.2925889),
98+
(-1.9828257, 49.2924815),
99+
(-1.9827145, 49.2925089),
100+
(-1.9828543, 49.2927771),
101+
(-1.9830398, 49.2927514),
102+
]
103+
),
104+
],
105+
[
106+
{"seamark:type": "restricted_area"},
107+
LineString(
108+
[
109+
(12.3569916, 47.9186626),
110+
(12.3567217, 47.9188108),
111+
(12.3564934, 47.9189565),
112+
(12.3564734, 47.9191199),
113+
(12.3565413, 47.9192803),
114+
(12.3568636, 47.919524),
115+
(12.3571719, 47.9196858),
116+
(12.3575482, 47.9197593),
117+
(12.3579399, 47.9198024),
118+
(12.3587152, 47.9200541),
119+
(12.3594448, 47.9203064),
120+
(12.3596907, 47.9203917),
121+
(12.3599993, 47.9204654),
122+
(12.3604107, 47.9205391),
123+
(12.3608174, 47.9205554),
124+
(12.3610279, 47.9205224),
125+
(12.3612053, 47.9204511),
126+
(12.3614394, 47.9201326),
127+
(12.3616484, 47.9198195),
128+
(12.3616249, 47.9196335),
129+
(12.361631, 47.9194503),
130+
(12.3616174, 47.9193071),
131+
(12.36156, 47.9192435),
132+
(12.3614394, 47.9191936),
133+
(12.3611173, 47.9191633),
134+
(12.3609535, 47.9190676),
135+
(12.3607335, 47.9189749),
136+
(12.3604259, 47.918891),
137+
(12.3595763, 47.9187613),
138+
(12.3587674, 47.9185358),
139+
(12.3584371, 47.9183784),
140+
(12.3582044, 47.9182997),
141+
(12.3579056, 47.918306),
142+
(12.3576587, 47.9183381),
143+
(12.3573105, 47.9184692),
144+
(12.3569916, 47.9186626),
145+
]
146+
),
147+
],
148+
],
149+
)
150+
151+
152+
@pytest.fixture(scope="class")
153+
def test_land_polygons_gdf():
154+
"""
155+
Fixture providing test land polygons GeoDataFrame for constraint tests.
156+
157+
:return: GeoDataFrame with test land polygon data
158+
:rtype: gpd.GeoDataFrame
159+
"""
160+
return gpd.GeoDataFrame(
161+
geometry=[box(4.056342603541809, 49.06378892560051, 8.748316591073674, 51.19862259935186)]
162+
)
163+
164+
165+
@pytest.fixture(scope="class")
166+
def continuous_check_database(tmp_path_factory, test_nodes_gdf, test_ways_gdf, test_land_polygons_gdf):
167+
"""
168+
Fixture providing a temporary SQLite database for ContinuousCheck tests.
169+
170+
Creates database in temporary directory with nodes, ways, and land_polygons layers.
171+
Database is automatically cleaned up after test class completes.
172+
173+
:param tmp_path_factory: pytest fixture for creating temporary directories
174+
:param test_nodes_gdf: GeoDataFrame with test nodes
175+
:type test_nodes_gdf: gpd.GeoDataFrame
176+
:param test_ways_gdf: GeoDataFrame with test ways
177+
:type test_ways_gdf: gpd.GeoDataFrame
178+
:param test_land_polygons_gdf: GeoDataFrame with test land polygons
179+
:type test_land_polygons_gdf: gpd.GeoDataFrame
180+
:yields: Database engine connected to temporary database
181+
:rtype: sqlalchemy.engine.Engine
182+
"""
183+
# Create temporary directory for this test class
184+
tmp_dir = tmp_path_factory.mktemp("continuous_check_db")
185+
db_path = tmp_dir / "gdfDB.sqlite"
186+
187+
# Create database engine
188+
engine = db.create_engine(f"sqlite:///{db_path}")
189+
190+
# Write GeoDataFrames to database
191+
test_nodes_gdf.to_file(str(db_path), driver="SQLite", layer="nodes", overwrite=True)
192+
test_ways_gdf.to_file(str(db_path), driver="SQLite", layer="ways", overwrite=True)
193+
test_land_polygons_gdf.to_file(str(db_path), driver="SQLite", layer="land_polygons", overwrite=True)
194+
195+
yield engine
196+
197+
# Cleanup (engine disposal)
198+
engine.dispose()
199+
# tmp_path_factory automatically cleans up the temporary directory
200+
201+
202+
@pytest.fixture(scope="class")
203+
def test_seamark_gdf():
204+
"""
205+
Fixture providing test seamark GeoDataFrame for route postprocessing tests.
206+
207+
:return: GeoDataFrame with test seamark data
208+
:rtype: gpd.GeoDataFrame
209+
"""
210+
return gpd.GeoDataFrame(
211+
columns=["tags", "geometry"],
212+
data=[
213+
[
214+
{"seamark:type": "separation_boundary"},
215+
LineString([(12, 0), (11, 3)]),
216+
],
217+
[
218+
{"seamark:type": "separation_boundary"},
219+
LineString([(11, 3), (5, 9)]),
220+
],
221+
[
222+
{"seamark:type": "separation_boundary"},
223+
LineString([(5, 9), (0, 6)]),
224+
],
225+
[
226+
{"seamark:type": "separation_line"},
227+
LineString([(11, 3), (8, 6), (5, 9)]),
228+
],
229+
[
230+
{"seamark:type": "separation_line"},
231+
LineString([(17, 7), (14, 10), (10, 14)]),
232+
],
233+
[
234+
{"seamark:type": "separation_zone"},
235+
LineString([(15, 6), (9, 12), (7, 10), (13, 4), (15, 6)]),
236+
],
237+
[
238+
{"seamark:type": "separation_lane"},
239+
LineString([(6, 10), (9, 7), (12, 4)]),
240+
],
241+
[
242+
{"seamark:type": "separation_lane"},
243+
LineString([(16, 6), (13, 9), (9, 13)]),
244+
],
245+
],
246+
)
247+
248+
249+
@pytest.fixture(scope="class")
250+
def route_postprocessing_database(tmp_path_factory, test_seamark_gdf):
251+
"""
252+
Fixture providing a temporary SQLite database for RoutePostprocessing tests.
253+
254+
Creates database in temporary directory with seamark layer.
255+
Database is automatically cleaned up after test class completes.
256+
257+
:param tmp_path_factory: pytest fixture for creating temporary directories
258+
:param test_seamark_gdf: GeoDataFrame with test seamark data
259+
:type test_seamark_gdf: gpd.GeoDataFrame
260+
:yields: Database engine connected to temporary database
261+
:rtype: sqlalchemy.engine.Engine
262+
"""
263+
# Create temporary directory for this test class
264+
tmp_dir = tmp_path_factory.mktemp("route_postprocessing_db")
265+
db_path = tmp_dir / "gdfDB.sqlite"
266+
267+
# Create database engine
268+
engine = db.create_engine(f"sqlite:///{db_path}")
269+
270+
# Write GeoDataFrame to database
271+
test_seamark_gdf.to_file(str(db_path), driver="SQLite", layer="seamark", overwrite=True)
272+
273+
yield engine
274+
275+
# Cleanup (engine disposal)
276+
engine.dispose()
277+
# tmp_path_factory automatically cleans up the temporary directory

0 commit comments

Comments
 (0)