Skip to main content

Opinionated mocking library for Python

Project description

Decoy

Opinionated mocking library for Python

Usage guide and documentation

The Decoy library allows you to create, stub, and verify fully-typed, async/await-friendly mocks in your Python unit tests, so your tests are:

  • Less prone to insufficient tests due to unconditional stubbing
  • Easier to fit into the Arrange-Act-Assert pattern
  • Covered by typechecking

The Decoy API is heavily inspired by / stolen from the excellent testdouble.js and Mockito projects.

Install

# pip
pip install decoy

# poetry
poetry add --dev 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:
    # ...

The decoy fixture is function-scoped and will ensure that all stub and spy state is reset between every test.

Mypy Setup

Decoy's API can be a bit confusing to mypy. To suppress mypy errors that may be emitted during valid usage of the Decoy API, we have a mypy plugin that you should add to your configuration file:

# mypy.ini

# ...
plugins = decoy.mypy
# ...

Basic Usage

This example assumes you are using pytest. See Decoy's documentation for a more detailed usage guide and API reference.

Define your test

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

from decoy import Decoy
from todo import TodoAPI, TodoItem
from todo.store TodoStore

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

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"))

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-1.6.3.tar.gz (17.2 kB view hashes)

Uploaded Source

Built Distribution

decoy-1.6.3-py3-none-any.whl (20.3 kB view hashes)

Uploaded Python 3

Supported by

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