Skip to content

Commit 98eac3c

Browse files
committed
test: generate bound model methods tests
1 parent 44d768f commit 98eac3c

File tree

1 file changed

+87
-0
lines changed

1 file changed

+87
-0
lines changed

tests/unit/conftest.py

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
from __future__ import annotations
44

5+
import inspect
6+
from typing import Callable, ClassVar
57
from unittest import mock
68

79
import pytest
@@ -130,3 +132,88 @@ def action_list_response(action1_running, action2_running):
130132
action2_running,
131133
],
132134
}
135+
136+
137+
def build_kwargs_mock(func: Callable) -> dict[str, mock.Mock]:
138+
"""
139+
Generate a kwargs dict that may be passed to the provided function for testing purposes.
140+
"""
141+
s = inspect.signature(func)
142+
143+
kwargs = {}
144+
for name, param in s.parameters.items():
145+
if name in ("self",):
146+
continue
147+
148+
if param.kind in (param.POSITIONAL_OR_KEYWORD, param.KEYWORD_ONLY):
149+
kwargs[name] = mock.Mock()
150+
continue
151+
152+
# Ignore **kwargs
153+
if param.kind in (param.VAR_KEYWORD,):
154+
continue
155+
156+
raise NotImplementedError(f"unsupported parameter kind: {param.kind}")
157+
158+
return kwargs
159+
160+
161+
def pytest_generate_tests(metafunc: pytest.Metafunc):
162+
"""
163+
Magic function to generate a test for each bound model method.
164+
"""
165+
if "bound_model_method" in metafunc.fixturenames:
166+
metafunc.parametrize("bound_model_method", metafunc.cls.methods)
167+
168+
169+
class BoundModelTestCase:
170+
methods: ClassVar[list[Callable]]
171+
172+
def test_method_list(self, bound_model):
173+
"""
174+
Ensure the list of bound model methods is up to date.
175+
"""
176+
members_count = 0
177+
members_missing = []
178+
for name, member in inspect.getmembers(
179+
bound_model,
180+
lambda m: inspect.ismethod(m)
181+
and m.__func__ in bound_model.__class__.__dict__.values(),
182+
):
183+
# Actions methods are already tested in TestBoundModelActions.
184+
if name in ("__init__", "get_actions", "get_actions_list"):
185+
continue
186+
187+
if member.__func__ in self.__class__.methods:
188+
members_count += 1
189+
else:
190+
members_missing.append(member.__func__.__qualname__)
191+
192+
assert not members_missing, "untested methods:\n" + ",\n".join(members_missing)
193+
assert members_count == len(self.__class__.methods)
194+
195+
def test_method(
196+
self,
197+
resource_client,
198+
bound_model,
199+
bound_model_method: Callable,
200+
):
201+
# Check if the resource client has a method named after the bound model method.
202+
assert hasattr(resource_client, bound_model_method.__name__)
203+
204+
# Mock the resource client method.
205+
resource_client_method_mock = mock.MagicMock()
206+
setattr(
207+
resource_client,
208+
bound_model_method.__name__,
209+
resource_client_method_mock,
210+
)
211+
212+
kwargs = build_kwargs_mock(bound_model_method)
213+
214+
# Call the bound model method
215+
result = getattr(bound_model, bound_model_method.__name__)(**kwargs)
216+
217+
resource_client_method_mock.assert_called_with(bound_model, **kwargs)
218+
219+
assert result is resource_client_method_mock.return_value

0 commit comments

Comments
 (0)