43 Allows to install mocks that are kept active between tests, see
44 @ref pytest_userver.plugins.grpc.mockserver.grpc_mockserver_session "grpc_mockserver_session".
46 @warning This is a sharp knife, use with caution! For most use-cases, prefer
47 @ref pytest_userver.plugins.grpc.mockserver.grpc_mockserver "grpc_mockserver" instead.
50 def __init__(self, *, server: grpc.aio.Server, experimental: bool =
False) ->
None:
52 @warning This initializer is an **experimental API**, likely to break in the future. Consider using
53 @ref pytest_userver.plugins.grpc.mockserver.grpc_mockserver_session "grpc_mockserver_session" instead.
55 Initializes `MockserverSession`. Takes ownership of `server`.
56 To properly start and stop the server, use `MockserverSession` as an async contextmanager:
59 async with pytest_userver.grpc.mockserver.MockserverSession(server=server) as mockserver:
63 @note `MockserverSession` is usually obtained from
64 @ref pytest_userver.plugins.grpc.mockserver.grpc_mockserver_session "grpc_mockserver_session".
71 def _get_auto_service_mock(self, servicer_class: type, /) -> _ServiceMock:
75 new_mock = _create_servicer_mock(servicer_class)
78 new_mock.add_to_server(self.
_server)
81 def _set_asyncexc_append(self, asyncexc_append: AsyncExcAppend |
None, /) ->
None:
84 mock.set_asyncexc_append(asyncexc_append)
88 @brief Removes all mocks for this mockserver that have been installed using
89 `MockserverSession` or @ref pytest_userver.grpc.Mockserver "Mockserver" API.
90 @note Mocks installed manually using @ref MockserverSession.server will not be removed, though.
95 @contextlib.contextmanager
98 Sets testsuite's `asyncexc_append` for use in the returned scope.
109 The underlying [grpc.aio.Server](https://grpc.github.io/grpc/python/grpc_asyncio.html#grpc.aio.Server).
116 @note Prefer starting mockserver using the async contextmanager syntax if possible.
122 Stops the server properly. Prefer this method to stopping `server` manually.
124 await _stop_server(self.
_server)
126 async def __aenter__(self) -> MockserverSession:
130 async def __aexit__(self, exc_type, exc_val, exc_tb) -> None:
136 Allows to install mocks that are reset between tests, see
137 @ref pytest_userver.plugins.grpc.mockserver.grpc_mockserver "grpc_mockserver".
139 Acts as a scope for the installed mocks. Mocks are removed on `__exit__` or @ref reset_mocks call.
142 def __init__(self, *, mockserver_session: MockserverSession, experimental: bool =
False) ->
None:
144 @warning This initializer is an **experimental API**, likely to break in the future. Consider using
145 @ref pytest_userver.plugins.grpc.mockserver.grpc_mockserver "grpc_mockserver" instead.
147 Initializes Mockserver. Should be used together with context manager syntax (`with` block).
149 @note `Mockserver` is usually obtained from
150 @ref pytest_userver.plugins.grpc.mockserver.grpc_mockserver "grpc_mockserver".
152 An example on creating a custom scope for gRPC mocks:
153 @snippet grpc/functional_tests/middleware_client/tests/test_custom_mockserver_session_scope.py global mock
160 def __call__(self, servicer_method, /) -> MockDecorator:
162 Returns a decorator to mock the specified gRPC service method implementation.
166 @snippet samples/grpc_service/testsuite/test_grpc.py Prepare modules
167 @snippet samples/grpc_service/testsuite/test_grpc.py grpc client test
169 servicer_class = _get_class_from_method(servicer_method)
171 return mock.install_handler(servicer_method.__name__, self.
_token)
173 def mock_factory(self, servicer_class, /) -> Callable[[str], MockDecorator]:
175 Allows to create a fixture as a shorthand for mocking methods of the specified gRPC service.
179 @snippet grpc/functional_tests/metrics/tests/conftest.py Prepare modules
180 @snippet grpc/functional_tests/metrics/tests/conftest.py Prepare server mock
181 @snippet grpc/functional_tests/metrics/tests/test_metrics.py grpc client test
184 def factory(method_name):
185 method = getattr(servicer_class, method_name,
None)
187 raise ValueError(f
'No method "{method_name}" in servicer class "{servicer_class.__name__}"')
190 _check_is_servicer_class(servicer_class)
195 Installs as a mock `servicer`, the class of which should inherit from a generated `*Servicer` class.
197 1. Write a service implementation:
199 @snippet grpc/functional_tests/middleware_client/tests/test_install_servicer.py servicer
201 @note Inheritance from multiple `*Servicer` classes at once is allowed.
203 2. Install servicer instance:
205 @snippet grpc/functional_tests/middleware_client/tests/test_install_servicer.py install servicer
209 @snippet grpc/functional_tests/middleware_client/tests/test_install_servicer.py use mock
211 base_servicer_classes = [cls
for cls
in inspect.getmro(type(servicer))
if _is_servicer_class(cls)]
212 if not base_servicer_classes:
213 raise ValueError(f
"Given object's type ({type(servicer)}) is not inherited from any grpc *Servicer class")
214 proxy = _MockProxy(servicer)
215 for servicer_class
in base_servicer_classes:
217 for python_method_name
in mock.known_methods:
218 if _get_class_that_defined_method(type(servicer), python_method_name)
not in base_servicer_classes:
219 handler_func: types.MethodType = getattr(servicer, python_method_name)
220 callqueue = mock.install_handler(python_method_name, token=self.
_token)(handler_func)
221 object.__setattr__(proxy, python_method_name, callqueue)
222 return typing.cast(Servicer, proxy)
226 Removes all mocks installed using this `Mockserver` instance.
229 mock.pop_handlers_for_token(self.
_token)
232 def __enter__(self) -> MockserverSession:
235 def __exit__(self, exc_type, exc_val, exc_tb) -> None:
238 def _get_service_mock(self, servicer_class: type, /) -> _ServiceMock:
247async def _stop_server(server: grpc.aio.Server, /) ->
None:
248 async def stop_server():
249 await server.stop(grace=
None)
250 await server.wait_for_termination()
252 stop_server_task = asyncio.shield(asyncio.create_task(stop_server()))
256 await stop_server_task
257 except asyncio.CancelledError:
258 await stop_server_task
263def _get_class_from_method(method) -> type:
265 assert inspect.isfunction(method), f
'Expected an unbound method: foo(ClassName.MethodName), got: {method}'
266 class_name = method.__qualname__.split(
'.<locals>', 1)[0].rsplit(
'.', 1)[0]
268 cls = getattr(inspect.getmodule(method), class_name)
269 except AttributeError:
270 cls = method.__globals__.get(class_name)
271 assert isinstance(cls, type)
275def _get_class_that_defined_method(cls: type, method_name: str) -> type |
None:
276 for cls
in inspect.getmro(cls):
277 if method_name
in cls.__dict__:
283def _is_servicer_class(cls: type) -> bool:
285 return '_pb2_grpc.' in inspect.getfile(cls)
290class _MockProxy(Generic[Servicer]):
291 def __init__(self, wrapped_servicer: Servicer) ->
None:
292 object.__setattr__(self,
'_wrapped_servicer', wrapped_servicer)
294 def __getattr__(self, name: str) -> Any:
295 servicer: Servicer = object.__getattribute__(self,
'_wrapped_servicer')
296 return getattr(servicer, name)
298 def __setattr__(self, name: str, value: Any) ->
None:
299 servicer: Servicer = object.__getattribute__(self,
'_wrapped_servicer')
300 setattr(servicer, name, value)