{"id":23525387,"url":"https://github.com/pyscript/umock","last_synced_at":"2025-05-14T08:11:07.936Z","repository":{"id":248738803,"uuid":"829420754","full_name":"pyscript/umock","owner":"pyscript","description":"MicroMock is a very simple and small module for mocking objects in MicroPython with PyScript.","archived":false,"fork":false,"pushed_at":"2024-09-09T09:13:12.000Z","size":110,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-05-11T04:57:35.288Z","etag":null,"topics":[],"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/pyscript.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":"CODE_OF_CONDUCT.md","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":"2024-07-16T11:50:28.000Z","updated_at":"2025-02-27T05:10:36.000Z","dependencies_parsed_at":"2024-07-26T10:43:08.322Z","dependency_job_id":"b5f001c5-2dfb-4868-80ee-9d9ab3b7bf0a","html_url":"https://github.com/pyscript/umock","commit_stats":null,"previous_names":["ntoll/umock","pyscript/umock"],"tags_count":1,"template":false,"template_full_name":"ntoll/codespaces-project-template-pyscript","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pyscript%2Fumock","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pyscript%2Fumock/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pyscript%2Fumock/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pyscript%2Fumock/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pyscript","download_url":"https://codeload.github.com/pyscript/umock/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254101559,"owners_count":22014908,"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":[],"created_at":"2024-12-25T19:08:44.027Z","updated_at":"2025-05-14T08:11:02.929Z","avatar_url":"https://github.com/pyscript.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# uMock (MicroMock) 🔬🥸\n\nMicroMock is a very simple and small module for mocking objects in MicroPython\nwith PyScript.\n\nIt currently only implements relatively naive versions of the following\nfeatures inspired by the `unittest.mock` module in the CPython standard\nlibrary.\n\n* A simplified `Mock` class to replace synchronous objects in Python.\n* A simplified `AsyncMock` class to replace `await`-able objects in Python.\n* A `patch` decorator / context manager to replace a target for the lifetime\n  of the decorated function / context manager.\n\n## Usage\n\n**This module is for use with MicroPython within PyScript.**\n\n### Setup\n\n1. Ensure the `umock.py` file is in your Python path. You may need to copy this\n   over using the \n   [files settings](https://docs.pyscript.net/2024.7.1/user-guide/configuration/#files).\n   You should probably ensure use of [uPyTest](https://github.com/ntoll/upytest)\n   by copying over the `upytest.py` file into your Python path.\n   (See the `config.json` file in this repository for an example of this in\n   action.)\n2. Create your tests that use mocks / patching, as described below.\n3. Ensure you have your tests setup properly as per the instruction in the\n   [upytest README](https://github.com/ntoll/upytest?tab=readme-ov-file#setup).\n4. In your `index.html` make sure you use the `async` and `terminal` attributes\n   referencing your MicroPython script (as in the `index.html` file in this\n   repository):\n\n   ```\n   \u003cscript type=\"mpy\" src=\"./main.py\" config=\"./config.json\" terminal async\u003e\u003c/script\u003e\n   ```\n\nNow point your browser at the `index.html` and you should see the test suite,\nincluding your mocks and patches, run.\n\n### Mocking\n\nIn code, a mock is simply something that imitates something else. Furthermore,\nmock objects often record their interactions with other aspects of the code so\nyou're able to observe and \"spy\" on the behaviour of your code, and perhaps\neven check expected behaviours are occuring.\n\nSuch objects are used in test situations when necessary objects are perhaps\nvery complicated to set up, or you only wish to test isolated code in a highly\nconstrained context without having to configure a complicated test setting.\n\nFor example, you may wish to mock away a database connection so the mock\nemulates a real database connection without the need for an expensive or\ncomplicated to configure database. All other aspects of the code under test\nremain the same.\n\nHowever, when using mocks, there is a danger you may mock away the universe and\nthe resulting context in which your test code is run doesn't accurately bear\nany resemblance to the real world.\n\nWith this context in mind, the `Mock` class provided by uMock is inspired by\n(but not the same as) Python's own unittest.mock.Mock class.\n\nThe main differences between this `Mock` class and Python's `unittest.mock.Mock`\nclass include:\n\n* Instantiation of the object only allows use of the `spec`, `side_effect` and\n  `return_value` keyword arguments (no `name`, `spec_set`, `wraps` or `unsafe`\n  arguments). However, arbitrary keyword arguments can be passed to become\n  attributes on the resulting mock object.\n* Calls are recorded in a list of tuples in the form `(args, kwargs)` rather\n  than a list of special `Call` instance objects.\n* Mock objects do NOT record nor reveal call information relating to thier\n  child mock objects (i.e. calls are not propagated to parent mocks).\n* None of the following methods exist in this implementation:\n  `mock_add_spec`, `attach_mock`, `configure_mock`, `_get_child_mock`,\n  `method_calls`, `mock_calls`.\n\nThe `Mock` class takes several optional arguments that specify the behaviour of\nthe Mock object:\n\n* `spec`: This can be either a list of strings or an existing object (a\n  class or instance) that acts as the specification for the mock\n  object. If you pass in an object then a list of strings is formed by\n  calling dir on the object (excluding unsupported magic attributes and\n  methods). Accessing any attribute not in this list will raise an\n  `AttributeError`.\n\n  If `spec` is an object (rather than a list of strings) then `__class__`\n  returns the class of the `spec` object. This allows mocks to pass\n  `isinstance()` tests.\n* `side_effect`: A function to be called whenever the Mock is called.\n  Useful for raising exceptions or dynamically changing return values.\n  The function is called with the same arguments as the mock, and the\n  return value of this function is used as the mock's return value.\n\n  Alternatively `side_effect` can be an exception class or instance. In\n  this case the exception will be raised when the mock is called.\n\n  If `side_effect` is an iterable then each call to the mock will return\n  the next value from the iterable.\n\n  A `side_effect` can be cleared by setting it to `None`.\n* `return_value`: The value returned when the mock is called. By default\n  this is a new Mock (created on first access).\n\nThe resulting mock object has the following properties:\n\n* `call_count`: the number of calls made to the mock object.\n* `called`: `True` if the mock object was called at least once.\n* `call_args`: the arguments of the last call to the mock object.\n* `call_args_list`: a list of the arguments of each call to the mock object.\n\nThe mock object also has the following methods:\n\n* `reset_mock()`: reset the mock object to a clean state. This is useful for\n  when you want to reuse a mock object.\n* `assert_called()`: assert that the mock object was called at least once.\n* `assert_called_once()`: assert that the mock object was called once.\n* `assert_called_with(*args, **kwargs)`: assert that the mock object was last\n  called with the specified arguments.\n* `assert_called_once_with(*args, **kwargs)`: assert that the mock object was \n  called once with the given arguments.\n* `assert_any_call(*args, **kwargs)`: assert that the mock object was called at \n  least once with the given arguments.\n* `assert_has_calls(calls, any_order=False)`: assert the mock has been called\n  with the specified `calls`. If `any_order` is `False` then the calls must be \n  sequential. If `any_order` is `True` then the calls can be in any order, but\n  they must all appear in `call_args_list`.\n* `assert_never_called()`: assert that the mock object was never called.\n\nAs a result, given a mock object it is possible to call it, have it behave in\na specified manner, and interrogate it about how it has been used:\n\n```python\nfrom umock import Mock\n\n\nm = Mock(return_value=42)\n\nmeaning_of_life = m()\n\nassert meaning_of_life == 42, \"Meaning of life is not H2G2 compliant.\"\nm.assert_called_once()\n```\n\n### Asynchronous Mocking\n\nAs with the `Mock` class, the `AsyncMock` class provided by uMock allows you to\ncreate and observe mock objects that, rather than being called, can be\n`await`-ed in Python (for when you're writing asynchronous code).\n\nThis class works in almost exactly the same way as the regular `Mock` class,\nbut instead of calling it, you `await` it. Furthermore, the properties and\nmethods on the `AsyncMock` class are named differently to reflect the\n`await`-able nature of the object.\n\nAn `AsyncMock` object has the following properties:\n\n* `await_count`: the number of times the mock object has been awaited.\n* `awaited`: `True` if the mock object was awaited at least once.\n* `await_args`: the arguments of the last await on the mock object.\n* `await_args_list`: a list of the arguments of each await on the mock object.\n\nAn asynchronous mock object also has the following methods:\n\n* `reset_mock()`: reset the mock object to a clean state. This is useful for\n  when you want to reuse a mock object.\n* `assert_awaited()`: assert that the mock object was awaited at least once.\n* `assert_awaited_once()`: assert that the mock object was awaited once.\n* `assert_awaited_with(*args, **kwargs)`: assert that the mock object was last\n  awaited with the specified arguments.\n* `assert_awaited_once_with(*args, **kwargs)`: assert that the mock object was\n  awaited once with the given arguments.\n* `assert_any_await(*args, **kwargs)`: assert that the mock object was awaited\n  at least once with the given arguments.\n* `assert_has_awaits(awaits, any_order=False)`: assert the mock has been\n  awaited with the specified awaits. If `any_order` is `False` then the awaits \n  must be sequential. If `any_order` is `True` then the awaits can be in any \n  order, but they must all appear in `await_args_list`.\n* `assert_not_awaited()`: assert that the mock object was never awaited.\n\nAn `AsyncMock` object can be awaited, respond in a specified manner, and you\ncan interrogate it about how it has been used:\n\n```python\nfrom umock import AsyncMock\n\n\nm = AsyncMock(return_value=42)\n\nmeaning_of_life = await m()\n\nassert meaning_of_life == 42, \"Meaning of life is not H2G2 compliant.\"\nm.assert_awaited_once()\n```\n\n### Patching\n\nThe `patch` class acts as a function decorator or a context manager. Inside the\nbody of the function or `with` statement, the target is patched with a new\nobject. When the function/with statement exits the patch is undone.\n\nThe `patch` must always have a target argument that identifies the Python\nobject to replace. This string much be of the form:\n\n`\"module.submodule:object_name.method_name\"`\n\n(Note the colon \":\"!)\n\nIf no `new` object is provided as the optional second argument, then a new Mock\nobject is created with the supplied `kwargs`.\n\nIf the `patch` class is being used as a decorator for a function, it will pass\nin the resulting Mock object as the function's argument.\n\n```python\nfrom umock import patch\n\n\n@patch(\"tests.a_package.a_module:a_function\", return_value=42)\ndef test(mock_object):\n    from tests.a_package.a_module import a_function\n\n    assert mock_object is a_function, \"Wrong object patched.\"\n    assert (\n        a_function(1, 2) == 42\n    ), \"Wrong return value from patched object.\"\n```\n\nAlternatively, if the `patch` class can used as a context manager.\n\n```python\nfrom umock import patch, Mock\n\n\nmock_function = Mock(return_value=42)\n\nwith patch(\"tests.a_package.a_module:a_function\", mock_function) as mock_object:\n    assert mock_object is mock_function, \"Wrong replacement object.\"\n    from tests.a_package.a_module import a_function\n\n    assert (\n        a_function(1, 2) == 42\n    ), \"Wrong return value from patched object.\"\n\nmock_function.assert_called_once_with(1, 2)\n```\n\n## Developer setup\n\nThis is easy:\n\n1. Clone the project.\n2. Start a local web server: `python -m http.server`\n3. Point your browser at http://localhost:8000/\n4. Change code and refresh your browser to check your changes.\n5. **DO NOT CREATE A NEW FEATURE WITHOUT FIRST CREATING AN ISSUE FOR IT IN WHICH\n   YOU PROPOSE YOUR CHANGE**. (We want to avoid a situation where you work hard\n   on something that is ultimately rejected by the maintainers.)\n6. Given all the above, pull requests are welcome and greatly appreciated.\n\nWe expect all contributors to abide by the spirit of our\n[code of conduct](./CODE_OF_CONDUCT.md).\n\n## Testing uMock\n\nSee the content of the `tests` directory in this repository. To run the test\nsuite, just follow steps 1, 2 and 3 in the developer setup section.\n\nWe use the [uPyTest](https://github.com/ntoll/upytest) to run our test suite.\n\n## License\n\nCopyright (c) 2024 Nicholas H.Tollervey\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies\nof the Software, and to permit persons to whom the Software is furnished to do\nso, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpyscript%2Fumock","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpyscript%2Fumock","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpyscript%2Fumock/lists"}