{ "info": { "author": "Denis Kataev", "author_email": "denis.a.kataev@gmail.com", "bugtrack_url": null, "classifiers": [ "Framework :: Pytest", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: MacOS :: MacOS X", "Operating System :: Microsoft :: Windows", "Operating System :: POSIX", "Programming Language :: Python", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Topic :: Software Development :: Quality Assurance", "Topic :: Software Development :: Testing", "Topic :: Utilities" ], "description": "# pytest-grpc\n\nWrite test for gRPC with pytest.\n\n\n## Exmaple\n\nSee example dir and/or read 'usage'.\n\n## Usage\n\nFor example you have some proto file with rpc declaration.\n\n\n```proto\nsyntax = \"proto3\";\n\npackage test.v1;\n\n\nservice EchoService {\n rpc handler(EchoRequest) returns (EchoResponse) {\n }\n}\n\n\nmessage EchoRequest {\n string name = 1;\n}\n\nmessage EchoResponse {\n string name = 1;\n}\n\n```\n\nAfter compile it with grpcio-tools, you get *_pb2.py and *_pb2_grpc.py files, now you can write your service.\n\n```python\nfrom stub.test_pb2 import EchoRequest, EchoResponse\nfrom stub.test_pb2_grpc import EchoServiceServicer\n\n\nclass Servicer(EchoServiceServicer):\n def handler(self, request: EchoRequest, context) -> EchoResponse:\n return EchoResponse(name=f'test-{request.name}')\n\n def error_handler(self, request: EchoRequest, context) -> EchoResponse:\n raise RuntimeError('Some error')\n```\n\nPoint pytest with your stubs and service:\n\n```python\nimport pytest\n\nfrom stub.test_pb2 import EchoRequest\n\n\n@pytest.fixture(scope='module')\ndef grpc_add_to_server():\n from stub.test_pb2_grpc import add_EchoServiceServicer_to_server\n\n return add_EchoServiceServicer_to_server\n\n\n@pytest.fixture(scope='module')\ndef grpc_servicer():\n from servicer import Servicer\n\n return Servicer()\n\n\n@pytest.fixture(scope='module')\ndef grpc_stub_cls(grpc_channel):\n from stub.test_pb2_grpc import EchoServiceStub\n\n return EchoServiceStub\n```\n\nWrite little test:\n```python\n\ndef test_some(grpc_stub):\n request = EchoRequest()\n response = grpc_stub.handler(request)\n\n assert response.name == f'test-{request.name}'\n\ndef test_example(grpc_stub):\n request = EchoRequest()\n response = grpc_stub.error_handler(request)\n\n assert response.name == f'test-{request.name}'\n``` \n\n#### Testing secure server\n\n```python\nfrom pathlib import Path\nimport pytest\nimport grpc\n\n@pytest.fixture(scope='module')\ndef grpc_add_to_server():\n from stub.test_pb2_grpc import add_EchoServiceServicer_to_server\n\n return add_EchoServiceServicer_to_server\n\n\n@pytest.fixture(scope='module')\ndef grpc_servicer():\n from servicer import Servicer\n\n return Servicer()\n\n\n@pytest.fixture(scope='module')\ndef grpc_stub_cls(grpc_channel):\n from stub.test_pb2_grpc import EchoServiceStub\n\n return EchoServiceStub\n\n\n@pytest.fixture(scope='session')\ndef my_ssl_key_path():\n return Path('/path/to/key.pem')\n\n\n@pytest.fixture(scope='session')\ndef my_ssl_cert_path():\n return Path('/path/to/cert.pem')\n\n\n@pytest.fixture(scope='module')\ndef grpc_server(_grpc_server, grpc_addr, my_ssl_key_path, my_ssl_cert_path):\n \"\"\"\n Overwrites default `grpc_server` fixture with ssl credentials\n \"\"\"\n credentials = grpc.ssl_server_credentials([\n (my_ssl_key_path.read_bytes(),\n my_ssl_cert_path.read_bytes())\n ])\n\n _grpc_server.add_secure_port(grpc_addr, server_credentials=credentials)\n _grpc_server.start()\n yield _grpc_server\n _grpc_server.stop(grace=None)\n\n\n@pytest.fixture(scope='module')\ndef my_channel_ssl_credentials(my_ssl_cert_path):\n # If we're using self-signed certificate it's necessarily to pass root certificate to channel\n return grpc.ssl_channel_credentials(\n root_certificates=my_ssl_cert_path.read_bytes()\n )\n\n\n@pytest.fixture(scope='module')\ndef grpc_channel(my_channel_ssl_credentials, create_channel):\n \"\"\"\n Overwrites default `grpc_channel` fixture with ssl credentials\n \"\"\"\n with create_channel(my_channel_ssl_credentials) as channel:\n yield channel\n\n\n@pytest.fixture(scope='module')\ndef grpc_authorized_channel(my_channel_ssl_credentials, create_channel):\n \"\"\"\n Channel with authorization header passed\n \"\"\"\n grpc_channel_credentials = grpc.access_token_call_credentials(\"some_token\")\n composite_credentials = grpc.composite_channel_credentials(\n my_channel_ssl_credentials,\n grpc_channel_credentials\n )\n with create_channel(composite_credentials) as channel:\n yield channel\n\n\n@pytest.fixture(scope='module')\ndef my_authorized_stub(grpc_stub_cls, grpc_channel):\n \"\"\"\n Stub with authorized channel\n \"\"\"\n return grpc_stub_cls(grpc_channel)\n\n```\n\n## Run tests agains real gRPC server\nRun tests against read grpc server worked in another thread:\n\n```bash\npy.test\n```\n\n```\ncachedir: .pytest_cache\nplugins: grpc-0.0.0\ncollected 2 items\n\nexample/test_example.py::test_some PASSED\nexample/test_example.py::test_example FAILED\n\n=================================== FAILURES ====================================\n_________________________________ test_example __________________________________\n\ngrpc_stub = \n\n def test_example(grpc_stub):\n request = EchoRequest()\n> response = grpc_stub.error_handler(request)\n\nexample/test_example.py:35:\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _\n.env/lib/python3.7/site-packages/grpc/_channel.py:547: in __call__\n return _end_unary_response_blocking(state, call, False, None)\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _\n\nstate = \ncall = \nwith_call = False, deadline = None\n\n def _end_unary_response_blocking(state, call, with_call, deadline):\n if state.code is grpc.StatusCode.OK:\n if with_call:\n rendezvous = _Rendezvous(state, call, None, deadline)\n return state.response, rendezvous\n else:\n return state.response\n else:\n> raise _Rendezvous(state, None, None, deadline)\nE grpc._channel._Rendezvous: <_Rendezvous of RPC that terminated with:\nE \tstatus = StatusCode.UNKNOWN\nE \tdetails = \"Exception calling application: Some error\"\nE \tdebug_error_string = \"{\"created\":\"@1544451353.148337000\",\"description\":\"Error received from peer\",\"file\":\"src/core/lib/surface/call.cc\",\"file_line\":1036,\"grpc_message\":\"Exception calling application: Some error\",\"grpc_status\":2}\"\nE >\n\n.env/lib/python3.7/site-packages/grpc/_channel.py:466: _Rendezvous\n------------------------------- Captured log call -------------------------------\n_server.py 397 ERROR Exception calling application: Some error\nTraceback (most recent call last):\n File \"pytest-grpc/.env/lib/python3.7/site-packages/grpc/_server.py\", line 389, in _call_behavior\n return behavior(argument, context), True\n File \"pytest-grpc/example/src/servicer.py\", line 10, in error_handler\n raise RuntimeError('Some error')\nRuntimeError: Some error\n================ 1 failed, 1 passed, 1 warnings in 0.16 seconds =================\n\n```\n\n## Run tests directly to python code\nCall handlers directly, with fake grpc internals:\n\n```bash\npy.test --grpc-fake-server\n```\n\nIn this case your get nice direct exceptions:\n\n```\n============================= test session starts =============================\ncachedir: .pytest_cache\nplugins: grpc-0.0.0\ncollected 2 items\n\nexample/test_example.py::test_some PASSED\nexample/test_example.py::test_example FAILED\n\n================================== FAILURES ===================================\n________________________________ test_example _________________________________\n\ngrpc_stub = \n\n def test_example(grpc_stub):\n request = EchoRequest()\n> response = grpc_stub.error_handler(request)\n\nexample/test_example.py:35:\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _\npytest_grpc/plugin.py:42: in fake_handler\n return real_method(request, context)\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _\n\nself = , request =\ncontext = \n\n def error_handler(self, request: EchoRequest, context) -> EchoResponse:\n> raise RuntimeError('Some error')\nE RuntimeError: Some error\n\nexample/src/servicer.py:10: RuntimeError\n=============== 1 failed, 1 passed, 1 warnings in 0.10 seconds ================\n```\n\n\n", "description_content_type": "text/markdown", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "", "keywords": "", "license": "MIT", "maintainer": "", "maintainer_email": "", "name": "pytest-grpc", "package_url": "https://pypi.org/project/pytest-grpc/", "platform": "linux", "project_url": "https://pypi.org/project/pytest-grpc/", "project_urls": null, "release_url": "https://pypi.org/project/pytest-grpc/0.7.0/", "requires_dist": [ "pytest (>=3.6.0)" ], "requires_python": "", "summary": "pytest plugin for grpc", "version": "0.7.0" }, "last_serial": 4790270, "releases": { "0.3.0": [ { "comment_text": "", "digests": { "md5": "24a5e14e522b5885cf01c3e0f0a50256", "sha256": "69b4efa332bac1c098a01b19456557011b06e021649de0e230adea5b8b5a3533" }, "downloads": -1, "filename": "pytest_grpc-0.3.0-py3-none-any.whl", "has_sig": false, "md5_digest": "24a5e14e522b5885cf01c3e0f0a50256", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 4428, "upload_time": "2018-12-10T14:31:59", "url": "https://files.pythonhosted.org/packages/19/0b/3359c71909dfb6bb658892c0680969e82f46c3b7fd74e54e818e3a450b10/pytest_grpc-0.3.0-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "e3ab83a858b6d81bd6f2a7de38aaa24a", "sha256": "ee284ed95eb8780bb14fe424f7e5f2a5e31a5896d8bd9382f7aaa46010dbfacd" }, "downloads": -1, "filename": "pytest-grpc-0.3.0.tar.gz", "has_sig": false, "md5_digest": "e3ab83a858b6d81bd6f2a7de38aaa24a", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 4300, "upload_time": "2018-12-10T14:32:00", "url": "https://files.pythonhosted.org/packages/ff/a1/c6c75aaa443a12afeb450a08a99a8d66e5a6a835bd0a7f5182eb28742d8d/pytest-grpc-0.3.0.tar.gz" } ], "0.4.0": [ { "comment_text": "", "digests": { "md5": "4612577ebafd8f7281263267dd778aec", "sha256": "2bf9ca36a342998245c3bae8d3680d1a7707950f86e77f555fcf914da77850b8" }, "downloads": -1, "filename": "pytest_grpc-0.4.0-py3-none-any.whl", "has_sig": false, "md5_digest": "4612577ebafd8f7281263267dd778aec", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 4463, "upload_time": "2019-01-24T13:11:11", "url": "https://files.pythonhosted.org/packages/65/9c/fe5e7020d9fddaa6c8a48d83f8a7656635edaacea20c46c0f09fbd55b813/pytest_grpc-0.4.0-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "f8cf4a0c1bcfd53bae4e6fa3ae5b4dac", "sha256": "a66287537b4e21c9373c7d789a5129c8b7e6775ecf43f9a90c131f7c374f76df" }, "downloads": -1, "filename": "pytest-grpc-0.4.0.tar.gz", "has_sig": false, "md5_digest": "f8cf4a0c1bcfd53bae4e6fa3ae5b4dac", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 4328, "upload_time": "2019-01-24T13:11:13", "url": "https://files.pythonhosted.org/packages/28/58/a07f8243bee924765916ead51b97a8ba61e3ed4e86a23914bd3fb27703c7/pytest-grpc-0.4.0.tar.gz" } ], "0.5.0": [ { "comment_text": "", "digests": { "md5": "7960615996bd3746dd7fac43dcf9b3f4", "sha256": "cc3ac46766cf9528c603178904c6809e3d0027a6e2c5d9454a4b976d0519d053" }, "downloads": -1, "filename": "pytest_grpc-0.5.0-py3-none-any.whl", "has_sig": false, "md5_digest": "7960615996bd3746dd7fac43dcf9b3f4", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 4487, "upload_time": "2019-01-30T10:47:29", "url": "https://files.pythonhosted.org/packages/bb/2d/018809374d13966d42fac01b024f29a436e3070a713a005a13bf73ee4b5c/pytest_grpc-0.5.0-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "d351c250709add877f39722b72a1eca6", "sha256": "386bfae4bb614ed02eec688661a1c92413b0ba04dd9c0c4c51c6eb7f2946cf90" }, "downloads": -1, "filename": "pytest-grpc-0.5.0.tar.gz", "has_sig": false, "md5_digest": "d351c250709add877f39722b72a1eca6", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 4364, "upload_time": "2019-01-30T10:47:30", "url": "https://files.pythonhosted.org/packages/ef/9f/aaf4f7fa3e75bec8b869f26d17fc1af64e0fac1aabd7931d78fbc4e56ddf/pytest-grpc-0.5.0.tar.gz" } ], "0.6.0": [ { "comment_text": "", "digests": { "md5": "20ee5d0dfabefab8054740804f964558", "sha256": "5a97dd461b4ff2903f6989d4f79e3ee4ec6011d356dbf4e26a6d3f053eb29b58" }, "downloads": -1, "filename": "pytest_grpc-0.6.0-py3-none-any.whl", "has_sig": false, "md5_digest": "20ee5d0dfabefab8054740804f964558", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 5268, "upload_time": "2019-02-05T06:42:04", "url": "https://files.pythonhosted.org/packages/d8/0d/ea4afe3a67f859189c29742c484ecc8a16977970af41c0ed1fe45a8889b9/pytest_grpc-0.6.0-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "428b9a1e09d21572309f618657719e8b", "sha256": "f9ccfa5400a87111de64ad3138864c6f94eeea89f4e8a36de48e3e01eb7d32ea" }, "downloads": -1, "filename": "pytest-grpc-0.6.0.tar.gz", "has_sig": false, "md5_digest": "428b9a1e09d21572309f618657719e8b", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 5276, "upload_time": "2019-02-05T06:42:06", "url": "https://files.pythonhosted.org/packages/5c/ed/88edacbc2dbb7d25875f479bf0b43c212a6c6a252258a84baef3bde78c03/pytest-grpc-0.6.0.tar.gz" } ], "0.7.0": [ { "comment_text": "", "digests": { "md5": "5e642d79072bfe9e9e206110065b99d4", "sha256": "28d75d2eea55518327289690053679b15ae867e54e7dff184c36316766b745ce" }, "downloads": -1, "filename": "pytest_grpc-0.7.0-py3-none-any.whl", "has_sig": false, "md5_digest": "5e642d79072bfe9e9e206110065b99d4", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 5262, "upload_time": "2019-02-07T09:52:57", "url": "https://files.pythonhosted.org/packages/cc/58/2df20206e69e0f8b6eacdf7650775da8510c9d8e15eb4c2dd28ba6dbece4/pytest_grpc-0.7.0-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "f05c5a5877620cb35283145a50fff3d2", "sha256": "6884dea2279c874be59dccc25d69aa93cf7e00e1636dbe23b84144a8f81095fa" }, "downloads": -1, "filename": "pytest-grpc-0.7.0.tar.gz", "has_sig": false, "md5_digest": "f05c5a5877620cb35283145a50fff3d2", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 5283, "upload_time": "2019-02-07T09:52:59", "url": "https://files.pythonhosted.org/packages/0d/a7/4c59a6684a73d507d5d2f5b87f10ff4bfe534c4d0df3ce1a690ab2017f59/pytest-grpc-0.7.0.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "5e642d79072bfe9e9e206110065b99d4", "sha256": "28d75d2eea55518327289690053679b15ae867e54e7dff184c36316766b745ce" }, "downloads": -1, "filename": "pytest_grpc-0.7.0-py3-none-any.whl", "has_sig": false, "md5_digest": "5e642d79072bfe9e9e206110065b99d4", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 5262, "upload_time": "2019-02-07T09:52:57", "url": "https://files.pythonhosted.org/packages/cc/58/2df20206e69e0f8b6eacdf7650775da8510c9d8e15eb4c2dd28ba6dbece4/pytest_grpc-0.7.0-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "f05c5a5877620cb35283145a50fff3d2", "sha256": "6884dea2279c874be59dccc25d69aa93cf7e00e1636dbe23b84144a8f81095fa" }, "downloads": -1, "filename": "pytest-grpc-0.7.0.tar.gz", "has_sig": false, "md5_digest": "f05c5a5877620cb35283145a50fff3d2", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 5283, "upload_time": "2019-02-07T09:52:59", "url": "https://files.pythonhosted.org/packages/0d/a7/4c59a6684a73d507d5d2f5b87f10ff4bfe534c4d0df3ce1a690ab2017f59/pytest-grpc-0.7.0.tar.gz" } ] }