# -*- coding: utf-8 -*-
from setuptools import setup

packages = \
['decoy']

package_data = \
{'': ['*']}

setup_kwargs = {
    'name': 'decoy',
    'version': '1.1.0',
    'description': 'Opinionated, typed stubbing and verification library for Python',
    'long_description': '# Decoy\n\n[![ci badge][]][ci]\n[![pypi version badge][]][pypi]\n[![license badge][]][license]\n\n[ci]: https://github.com/mcous/decoy/actions\n[ci badge]: https://flat.badgen.net/github/checks/mcous/decoy/main\n[pypi]: https://pypi.org/project/decoy/\n[pypi version badge]: https://flat.badgen.net/pypi/v/decoy\n[license]: https://github.com/mcous/decoy/blob/main/LICENSE\n[license badge]: https://flat.badgen.net/github/license/mcous/decoy\n\n> Opinionated, typed stubbing and verification library for Python\n\n<https://mike.cousins.io/decoy/>\n\nThe Decoy library allows you to create, stub, and verify test double objects for your Python unit tests, so your tests are:\n\n-   Less prone to insufficient tests due to unconditional stubbing\n-   Covered by typechecking\n-   Easier to fit into the Arrange-Act-Assert pattern\n\nThe Decoy API is heavily inspired by / stolen from the excellent [testdouble.js][] and [Mockito][] projects.\n\n[testdouble.js]: https://github.com/testdouble/testdouble.js\n[mockito]: https://site.mockito.org/\n\n## Install\n\n```bash\n# pip\npip install decoy\n\n# poetry\npoetry add --dev decoy\n```\n\n## Setup\n\nYou\'ll want to create a test fixture to reset Decoy state between each test run. In [pytest][], you can do this by using a fixture to create a new Decoy instance for every test.\n\nThe examples below assume the following global test fixture:\n\n```python\nimport pytest\nfrom decoy import Decoy\n\n@pytest.fixture\ndef decoy() -> Decoy:\n    return Decoy()\n```\n\nWhy is this important? The `Decoy` container tracks every fake that is created during a test so that you can define assertions using fully-typed rehearsals of your test double. It\'s important to wipe this slate clean for every test so you don\'t leak memory or have any state preservation between tests.\n\n[pytest]: https://docs.pytest.org/\n\n### Mypy Setup\n\nDecoy\'s rehearsal syntax can be a bit confusing to [mypy][] if the mock in question is supposed to return `None`. Normally, [mypy will complain][] if you try to use a `None`-returning expression as a value, because this is almost always a mistake.\n\nIn Decoy, however, it\'s an intentional part of the API and _not_ a mistake. To suppress these errors, Decoy provides a mypy plugin that you should add to your configuration file:\n\n```ini\n# mypi.ini\n\n# ...\nplugins = decoy.mypy\n# ...\n```\n\n[mypy]: https://mypy.readthedocs.io/\n[mypy will complain]: https://mypy.readthedocs.io/en/stable/error_code_list.html#check-that-called-function-returns-a-value-func-returns-value\n\n## Usage\n\n### Stubbing\n\nA stub is a an object used in a test that is pre-configured to return a result or raise an error if called according to a specification. In Decoy, you specify a stub\'s call expectations with a "rehearsal", which is simply a call to the stub inside of a `decoy.when` wrapper.\n\nBy pre-configuring the stub with specific rehearsals, you get the following benefits:\n\n-   Your test double will only return your mock value **if it is called correctly**\n-   You avoid separate "set up mock return value" and "assert mock called correctly" steps\n-   If you annotate your test double with an actual type, the rehearsal will fail typechecking if called incorrectly\n\n```python\nimport pytest\nfrom typing import cast, Optional\nfrom decoy import Decoy\n\nfrom .database import Database, Model\n\ndef get_item(uid: str, db: Database) -> Optional[Model]:\n  return db.get_by_id(uid)\n\ndef test_get_item(decoy: Decoy):\n    mock_item = cast(Model, { "foo": "bar" })\n    mock_db = decoy.create_decoy(spec=Database)\n\n    # arrange stub using rehearsals\n    decoy.when(mock_db.get_by_id("some-id")).then_return(mock_item)\n\n    # call code under test\n    some_result = get_item("some-id")\n    other_result = get_item("other-id")\n\n    # assert code result\n    assert some_result == mock_item\n    assert other_result is None\n```\n\n### Verifying interactions\n\nIf you\'re coming from `unittest.mock`, you\'re probably used to calling your code under test and _then_ verifying that your dependency was called correctly. Decoy provides similar call verification using the same "rehearsal" mechanism that the stubbing API uses.\n\n```python\nimport pytest\nfrom typing import cast, Optional\nfrom decoy import Decoy, verify\n\nfrom .logger import Logger\n\ndef log_warning(msg: str, logger: Logger) -> None:\n    logger.warn(msg)\n\ndef test_log_warning(decoy: Decoy):\n    logger = decoy.create_decoy(spec=Logger)\n\n    # call code under test\n    some_result = log_warning("oh no!", logger)\n\n    # verify double called correctly with a rehearsal\n    decoy.verify(logger.warn("oh no!"))\n```\n\nAsserting that calls happened after the fact can be useful, but **should only be used if the dependency is being called solely for its side-effect(s)**. Verification of interactions in this manner should be considered a last resort, because:\n\n-   If you\'re calling a dependency to get data, then you can more precisely describe that relationship using [stubbing](#stubbing)\n-   Side-effects are harder to understand and maintain than pure functions, so in general you should try to side-effect sparingly\n\nStubbing and verification of a decoy are **mutually exclusive** within a test. If you find yourself wanting to both stub and verify the same decoy, then one or more of these is true:\n\n-   The assertions are redundant\n-   The dependency is doing too much based on its input (e.g. side-effecting _and_ calculating complex data) and should be refactored\n\n### Usage with async/await\n\nDecoy supports async/await out of the box! Pass your async function or class with async methods to `spec` in `decoy.create_decoy_func` or `decoy.create_decoy`, respectively, and Decoy will figure out the rest.\n\nWhen writing rehearsals on async functions and methods, remember to include the `await` with your rehearsal call:\n\n```py\ndecoy.when(await mock_db.get_by_id("some-id")).then_return(mock_item)\n```\n\n### Matchers\n\nSometimes, when you\'re stubbing or verifying calls (or really when you\'re doing any sort of equality assertion in a test), you need to loosen a given assertion. For example, you may want to assert that a dependency is called with a string, but you don\'t care about the full contents of that string.\n\nDecoy includes a set of matchers, which are simply Python classes with `__eq__` methods defined, that you can use in rehearsals and/or assertions.\n\n```python\nimport pytest\nfrom typing import cast, Optional\nfrom decoy import Decoy, matchers\n\nfrom .logger import Logger\n\ndef log_warning(msg: str, logger: Logger) -> None:\n    logger.warn(msg)\n\ndef test_log_warning(decoy: Decoy):\n    logger = decoy.create_decoy(spec=Logger)\n\n    # call code under test\n    some_result = log_warning(\n        "Oh no, something went wrong with request ID abc123efg456",\n        logger=logger\n    )\n\n    # verify double called correctly\n    decoy.verify(\n        logger.warn(matchers.StringMatching("request ID abc123efg456"))\n    )\n```\n',
    'author': 'Mike Cousins',
    'author_email': 'mike@cousins.io',
    'maintainer': None,
    'maintainer_email': None,
    'url': 'https://mike.cousins.io/decoy/',
    'packages': packages,
    'package_data': package_data,
    'python_requires': '>=3.7,<4.0',
}


setup(**setup_kwargs)
