Skip to main content

Funk: A mocking framework for Python

Funk is a mocking framework for Python, influenced heavily by JMock. Funk helps to test modules in isolation by allowing mock objects to be used in place of “real” objects. Funk is licensed under the 2-clause BSD licence.

Installation

$ pip install funk

Example

Suppose we have an API for a file storage service. We want to list the names of all files, but the API limits the number of names it will return at a time. Therefore, we need to write some code that will keep making requests to the API until all names have been retrieved.

def fetch_names(file_storage):
    has_more = True
    token = None
    names = []

    while has_more:
        response = file_storage.names(token=token)
        names += response.names
        token = response.next_token
        has_more = token is not None

    return names


import funk

@funk.with_mocks
def test_request_for_names_until_all_names_are_fetched(mocks):
    file_storage = mocks.mock(FileStorage)

    mocks.allows(file_storage).names(token=None).returns(mocks.data(
        next_token="<token 1>",
        names=["a", "b"],
    ))
    mocks.allows(file_storage).names(token="<token 1>").returns(mocks.data(
        next_token="<token 2>",
        names=["c", "d"],
    ))
    mocks.allows(file_storage).names(token="<token 2>").returns(mocks.data(
        next_token=None,
        names=["e"],
    ))

    assert fetch_names(file_storage) == ["a", "b", "c", "d", "e"]

By using a mock object instead of a real instance of FileStorage, we can run our tests without a running instance of the file storage system. We also avoid relying on the implementation of FileStorage, making our tests more focused and less brittle.

If you’re using pytest, the easiest way to use Funk is as a fixture:

import funk
import pytest

@pytest.yield_fixture
def mocks():
    mocks = funk.Mocks()
    yield mocks
    mocks.verify()

def test_request_for_names_until_all_names_are_fetched(mocks):
    file_storage = mocks.mock(FileStorage)
    ...

Usage

Creating a mock context

Create an instance of Mocks to allow mock objects to be created. Call Mocks.verify() to assert that all expectations have been met.

import funk

def test_case():
    mocks = funk.Mocks()
    ...
    mocks.verify()

Use the decorator funk.with_mocks to inject a mocks argument to a function. verify() will be automatically invoked at the end of the function.

import funk

@funk.with_mocks
def test_case(mocks):
    ...

If using pytest, a fixture is the simplest way to use Funk:

import funk
import pytest

@pytest.yield_fixture
def mocks():
    mocks = funk.Mocks()
    yield mocks
    mocks.verify()

def test_case(mocks):
    ...

Creating mock objects

Call Mocks.mock() to create a mock object.

file_storage = mocks.mock()

If the base argument is passed, only methods on that type can be mocked:

file_storage = mocks.mock(FileStorage)

This can be useful to ensure that only existing methods are mocked, but should be avoided if generating methods dynamically, such as by using __getattr__.

Set the name argument to set the name that should be used in assertion failure messages for the mock:

file_storage = mocks.mock(name="file_storage")

Setting expectations

To set up an expectation, use funk.allows() or funk.expects(). For convenience, these functions are also available on Mocks. funk.allows() will let the method be called any number of times, including none. funk.expects() will ensure that the method is called exactly once. For instance:

allows(file_storage).names

This allows the method file_storage.names to be called with any arguments any number of times. To only allow calls with specific arguments, you can invoke .names as a method:

allows(file_storage).names(token="<token 1>")

This will only allow calls with a matching token keyword argument, and no other arguments.

You can also use matchers from Precisely to match arguments:

from precisely import instance_of

allows(file_storage).names(token=instance_of(str))

If more than one expectation is set up on the same method, the first matching expectation is used. If you need to enforce methods being called in a particular order, use sequences.

Actions

By default, a mocked method returns None. Use returns() to return a different value:

allows(file_storage).names().returns([])

Use raises() to raise an exception:

allows(file_storage).names().raises(Exception("Could not connect"))

Sequences

A sequence object can be created using Mocks.sequence. The sequencing on objects can then be defined using in_sequence(sequence) when setting expectations. For instance:

file_storage = mocks.mock(FileStorage)
file_ordering = mocks.sequence()

expects(file_storage).save(NAME_1, CONTENTS_1).in_sequence(file_ordering)
expects(file_storage).save(NAME_2, CONTENTS_2).in_sequence(file_ordering)

Metadata

Release files for Funk 0.5.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for Funk 0.5.0
File Size Uploaded
Funk-0.5.0.tar.gz 22.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for Funk 0.5.0
File Interpreter ABI Platform
Funk-0.5.0-py2.py3-none-any.whl Python 3, Python 2 none any Details

Total release size: 33.7 kB

Release files / Funk-0.5.0.tar.gz

Download URL Funk-0.5.0.tar.gz
Size 22.9 kB
Tags Source
SHA-256 checksum
How to use checksums
59e954b744dacc28785bd81abe11dd9957dc5a4774db949b573f2c3861cfe84c
BLAKE2b-256 checksum
How to use checksums
8d25fbf23cd90e3fac6068d6dd60110108f174f9a0d9a142aa6d86d8e5a1be74
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via Python-urllib/2.7

Release files / Funk-0.5.0-py2.py3-none-any.whl

Download URL Funk-0.5.0-py2.py3-none-any.whl
Size 10.8 kB
Tags Python 2 Python 3
SHA-256 checksum
How to use checksums
62eeaa238b668171756e75b5bda8a02cea86e8d55c55f4d0417f52246d6f9101
BLAKE2b-256 checksum
How to use checksums
b160e8f3dbe06a3c858a131c6ee3ec11b7a10c313b0c2a64519d889aee8fe647
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via Python-urllib/2.7

Release history Release notifications | RSS feed

This release

0.5.0 This release

2 release files

0.4.0

1 release file

0.3.1

1 release file

0.3

1 release file

0.2

1 release file

0.1

1 release file

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page