{"id":16546979,"url":"https://github.com/pohmelie/pytest-automock","last_synced_at":"2025-06-23T13:33:18.694Z","repository":{"id":46902770,"uuid":"220486674","full_name":"pohmelie/pytest-automock","owner":"pohmelie","description":"Automock fixtures for pytest","archived":false,"fork":false,"pushed_at":"2023-05-16T21:32:22.000Z","size":67,"stargazers_count":16,"open_issues_count":1,"forks_count":1,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-05-17T20:04:51.754Z","etag":null,"topics":["mocks","plugin","pytest"],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/pohmelie.png","metadata":{"files":{"readme":"README.md","changelog":"history.md","contributing":null,"funding":null,"license":"license.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-11-08T14:45:20.000Z","updated_at":"2024-12-08T15:32:51.000Z","dependencies_parsed_at":"2024-10-28T10:17:56.319Z","dependency_job_id":"06a44b20-8b4b-477b-81bc-f91090db83e6","html_url":"https://github.com/pohmelie/pytest-automock","commit_stats":{"total_commits":38,"total_committers":5,"mean_commits":7.6,"dds":0.368421052631579,"last_synced_commit":"8afea2162761f00530f790cab6a10087689eb990"},"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/pohmelie/pytest-automock","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pohmelie%2Fpytest-automock","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pohmelie%2Fpytest-automock/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pohmelie%2Fpytest-automock/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pohmelie%2Fpytest-automock/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pohmelie","download_url":"https://codeload.github.com/pohmelie/pytest-automock/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pohmelie%2Fpytest-automock/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":260733766,"owners_count":23054324,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["mocks","plugin","pytest"],"created_at":"2024-10-11T19:13:14.797Z","updated_at":"2025-06-23T13:33:13.679Z","avatar_url":"https://github.com/pohmelie.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# pytest-automock\n[![Github actions status for master branch](https://github.com/pohmelie/pytest-automock/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/pohmelie/pytest-automock/actions)\n[![Codecov coverage for master branch](https://codecov.io/gh/pohmelie/pytest-automock/branch/master/graph/badge.svg)](https://codecov.io/gh/pohmelie/pytest-automock)\n[![Pypi version](https://img.shields.io/pypi/v/pytest-automock.svg)](https://pypi.org/project/pytest-automock/)\n[![Pypi downloads count](https://img.shields.io/pypi/dm/pytest-automock)](https://pypi.org/project/pytest-automock/)\n\n# Reason\n* No generic automock solution\n\n# Features\n* Pytest plugin\n* Autogenerate/autouse mocks for functions and objects\n* Sync and async support\n* Locked mode to be sure mocked objects stay untouched\n* Customizable serialization\n\n# Limitations\n* No support for dunder methods (can be partly solved in future)\n* No support for sync/async generators/contexts\n* Races can break tests, since order counts\n* Non-determenistic representation will break tests, since representation is a part of call snapshot key\n\n# License\n`pytest-automock` is offered under MIT license.\n\n# Requirements\n* python 3.6+\n\n# Usage\nLets say you have some module `mymod.py`:\n``` python\nimport time\n\nclass Network:\n    def get_data_from_network(self, x, y):\n        time.sleep(1)\n        return x + y\n\n    def send_data_to_network(self, value):\n        time.sleep(1)\n\ndef logic(x):\n    n = Network()\n    a, b = 0, 1\n    while b \u003c x:\n        c = n.get_data_from_network(a, b)\n        a, b = b, c\n        n.send_data_to_network(\"ok\")\n    return b\n```\nAnd you want to create mocks for your `Network` class (since testing time and sane counts), but you are too lazy to write them... `conftest.py`:\n``` python\nimport pytest\nimport mymod\n\n@pytest.fixture(autouse=True)\ndef _mocks(automock):\n    with automock((mymod, \"Network\")):\n        yield\n```\nYou can also use module path notation:\n``` python\nimport pytest\n\n@pytest.fixture(autouse=True)\ndef _mocks(automock):\n    with automock(\"mymod.Network\"):\n        yield\n```\n`test_logic.py`:\n``` python\nfrom mymod import logic\n\ndef test_logic():\n    assert logic(7) == 8\n    assert logic(10) == 13\n```\nIf you run `pytest` on this setup, then you will see fail:\n``` bash\n$ pytest -x\n...\nE           RuntimeError: Mock is locked, but '__init__' wanted\n```\n`automock` can work in two modes: locked and unlocked. Locked mode is default, real methods calls of mocked objects are\nnot allowed in this mode. So, above error says that we can't call `__init__` of our `Network`.\nIn locked mode there is no mock-files update also.\n\nTo allow real calls and mocks generation `automock` provides extra cli argument to `pytest`: `--automock-unlocked`\n``` bash\n$ pytest -x --automock-unlocked\n...\ntest_logic.py .\n...\n1 passed in 22.09s\n```\nAfter that you can see that `tests/mocks/test_logic/mymod/Network` file was created. This is mock for your test sequence.\nNow you can rerun tests and see what happens (you can omit `--automock-unlocked` key for ensurance, that real object\nwill not be touched (actually even created)).\n``` bash\n$ pytest -x\n...\ntest_logic.py .\n...\n1 passed in 0.04s\n```\n# API\n## `automock` (fixture)\n`automock` fixture is a **context manager**\n```python\ndef automock(*targets,\n             storage: Union[str, Path] = \"tests/mocks\",\n             override_name: Optional[str] = None,\n             unlocked: Optional[bool] = None,\n             remove: Optional[bool] = None,\n             encode: Callable[[Any], bytes] = default_encode,\n             decode: Callable[[bytes], Any] = default_decode,\n             debug: Optional[Callable[[Dict, Call, Optional[Call]], None]] = None)\n```\n* `*targets`: pair/tuple of object/module and attribute name (`str`) or module path to object/function with dot delimiter (`(mymod, \"Network\")` or `\"mymod.Network\"`)\n* `storage`: root path for storing mocks\n* `override_name`: forced mock-file name\n* `unlocked`: mode selector (if omited, selected by `--automock-unlocked`)\n* `remove`: remove test mock before test run (if omited, selected by `--automock-remove`)\n* `encode`: encode routine\n* `decode`: decode routine\n* `debug`: function for debugging failed cases, when you do not understand why automock bakes. Arguments are:\n    * `memory` for current failed test\n    * `call_wanted` which is a call you want to do right now\n    * `call_saved` which is a call you saved last time you generate mocks for this test\n\n`call_wanted` and `call_saved` are rich `Call` class objects, inspect it in `mock.py` file. Also, you can use a `\"pdb\"` string instead of your own function as a `debug` argument value, to use internal function with `pdb.set_trace()` instruction. Default `encode`/`decode` routine uses `pickle` and `gzip`.\n\n## `automock_unlocked` (fixture)\nFixture with default mode from cli parameter (`bool`).\n\n## `automock_remove` (fixture)\nFixture with default mode from cli parameter (`bool`).\n\n## `automock` (function)\n`automock` function is not supposed to be used by anyone but `automock` fixture\n``` python\ndef automock(factory: Callable, *,\n             memory: Dict,\n             locked: bool = True,\n             encode: Callable[[Any], bytes] = default_encode,\n             decode: Callable[[bytes], Any] = default_decode,\n             debug: Optional[Callable[[Dict, Call, Optional[Call]], None]] = None):\n```\n* `factory`: object/function to wrap\n* `memory`: dicrionary to get/put mocks\n* `locked`: mode selector\n* `encode`: encode routine\n* `decode`: decode routine\n* `debug`: same as for ficture\n\nDefault `encode`/`decode` routine uses `pickle` and `gzip`.\n\n# Caveats\n## Order\nAs feature paragraph described: «order counts». What does it mean?\n\n### Functions\nMocked functions/coroutines call order counts. If you mock sequence\n``` python\nfunc(1, 2)\nfunc(2, 3)\n```\nand trying to use mocked data with sequence\n``` python\nfunc(2, 3)\nfunc(1, 2)\n```\nYou will get an error, since calling order is part of idea of deterministic tests\n\n### Objects\nMocked objects have same behavior, but methods call are individual, so if you mock sequence\n``` python\nt1 = T(1)\nt2 = T(2)\nt1.func(1, 2)\nt2.func(2, 3)\n```\nthen calling order are individual for method calls, so this is ok:\n``` python\nt1 = T(1)\nt2 = T(2)\nt2.func(2, 3)\nt1.func(1, 2)\n```\nBut not for `__init__` method, since mocks are internaly attached to instance\n``` python\nt2 = T(2)\nt1 = T(1)\nt1.func(1, 2)\nt2.func(2, 3)\n```\nwill fail\n\n## Function arguments\nInternally, key for mocks consists of instance number and call number. This leads to some «unobvious» behavior:\n``` python\nimport time\nfrom pytest_automock import automock\n\ndef nop(x):\n    return x\n\nm = {}\nmocked = automock(nop, memory=m, locked=False)\nmocked(time.time())\n\nmocked = automock(nop, memory=m, locked=True)\nmocked(time.time())\n```\nWill fail because of argument in mock creation time differs from argument in mock use time. Same thing will break mocks if pickled representation is not determenistic.\n\n# Development\n## Run tests\nSince coverage issue/feature, plugins coverage is broken by default. [Workaround](https://pytest-cov.readthedocs.io/en/latest/plugins.html):\n``` bash\nCOV_CORE_SOURCE=pytest_automock COV_CORE_CONFIG=.coveragerc COV_CORE_DATAFILE=.coverage.eager pytest\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpohmelie%2Fpytest-automock","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpohmelie%2Fpytest-automock","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpohmelie%2Fpytest-automock/lists"}