Skip to main content

Opinionated mocking library for Python

Project description

Decoy logo

Decoy

Opinionated mocking library for Python

Usage guide and documentation

Decoy is a mocking library designed for effective and productive test-driven development in Python. If you want to use tests to guide the structure of your code, Decoy might be for you!

Decoy mocks are async/await and type-checking friendly. Decoy is heavily inspired by (and/or stolen from) the excellent testdouble.js and Mockito projects. The Decoy API is powerful, easy to read, and strives to help you make good decisions about your code.

Install

pip install decoy

Setup

Pytest setup

Decoy ships with its own pytest plugin, so once Decoy is installed, you're ready to start using it via its pytest fixture, called decoy.

# test_my_thing.py
from decoy import Decoy

def test_my_thing_works(decoy: Decoy) -> None:
    ...

Mypy setup

By default, Decoy is compatible with Python typing and type-checkers like mypy. However, stubbing functions that return None can trigger a type checking error during correct usage of the Decoy API. To suppress these errors, add Decoy's plugin to your mypy configuration.

# mypy.ini
plugins = decoy.mypy

Other testing libraries

Decoy works well with pytest, but if you use another testing library or framework, you can still use Decoy! You just need to do two things:

  1. Create a new instance of Decoy() before each test
  2. Call decoy.reset() after each test

For example, using the built-in unittest framework, you would use the setUp fixture method to do self.decoy = Decoy() and the tearDown method to call self.decoy.reset(). For a working example, see tests/test_unittest.py.

Basic Usage

This basic example assumes you are using pytest. For more detailed documentation, see Decoy's usage guide and API reference.

Decoy will add a decoy fixture to pytest that provides its mock creation API.

from decoy import Decoy

def test_something(decoy: Decoy) -> None:
    ...

!!! note

Importing the `Decoy` interface for type annotations is recommended, but optional. If your project does not use type annotations, you can simply write:

```python
def test_something(decoy):
    ...
```

Create a mock

Use decoy.mock to create a mock based on some specification. From there, inject the mock into your test subject.

def test_add_todo(decoy: Decoy) -> None:
    todo_store = decoy.mock(cls=TodoStore)
    subject = TodoAPI(store=todo_store)
    ...

See creating mocks for more details.

Stub a behavior

Use decoy.when to configure your mock's behaviors. For example, you can set the mock to return a certain value when called in a certain way using then_return:

def test_add_todo(decoy: Decoy) -> None:
    """Adding a todo should create a TodoItem in the TodoStore."""
    todo_store = decoy.mock(cls=TodoStore)
    subject = TodoAPI(store=todo_store)

    decoy.when(
        todo_store.add(name="Write a test for adding a todo")
    ).then_return(
        TodoItem(id="abc123", name="Write a test for adding a todo")
    )

    result = subject.add("Write a test for adding a todo")
    assert result == TodoItem(id="abc123", name="Write a test for adding a todo")

See stubbing with when for more details.

Verify a call

Use decoy.verify to assert that a mock was called in a certain way. This is best used with dependencies that are being used for their side-effects and don't return a useful value.

def test_remove_todo(decoy: Decoy) -> None:
    """Removing a todo should remove the item from the TodoStore."""
    todo_store = decoy.mock(cls=TodoStore)
    subject = TodoAPI(store=todo_store)

    subject.remove("abc123")

    decoy.verify(todo_store.remove(id="abc123"), times=1)

See spying with verify for more details.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

decoy-2.6.0.tar.gz (39.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

decoy-2.6.0-py3-none-any.whl (52.1 kB view details)

Uploaded Python 3

File details

Details for the file decoy-2.6.0.tar.gz.

File metadata

  • Download URL: decoy-2.6.0.tar.gz
  • Upload date:
  • Size: 39.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for decoy-2.6.0.tar.gz
Algorithm Hash digest
SHA256 e65c875c8cfaed6ceff55df39d266e2967e90de3390c99d228ff94c6a2ba51d7
MD5 f0449f115f102193d3ef830c387b65d2
BLAKE2b-256 ac1b17035a255625e9dc82a3ac3e73b228016275996b85fd163b10ea448dc3b2

See more details on using hashes here.

Provenance

The following attestation bundles were made for decoy-2.6.0.tar.gz:

Publisher: ci.yml on mcous/decoy

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file decoy-2.6.0-py3-none-any.whl.

File metadata

  • Download URL: decoy-2.6.0-py3-none-any.whl
  • Upload date:
  • Size: 52.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for decoy-2.6.0-py3-none-any.whl
Algorithm Hash digest
SHA256 c2d333a2ee3632d5bae907eca4bf7c341c287589cc37ebdfad51038e9bdcf192
MD5 ddbf4379f1f5fbcf39b55956e7dda3c1
BLAKE2b-256 532a813d13054e67c5dfc492c2cf2e51a5571a1dd026714d7b8d4a20064cb19f

See more details on using hashes here.

Provenance

The following attestation bundles were made for decoy-2.6.0-py3-none-any.whl:

Publisher: ci.yml on mcous/decoy

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page