Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow use of custom mock implementations #150

Open
antdking opened this issue Jun 27, 2019 · 7 comments
Open

Allow use of custom mock implementations #150

antdking opened this issue Jun 27, 2019 · 7 comments

Comments

@antdking
Copy link

With the growing use of asyncio, we need ways to mock Coroutines.

Currently, mock and unittest.mock do not support creating specs for coroutines (AsyncMock is being added in python 3.8).

As a work around, we're using asynctest.mock, which has the same interface as unittest.mock, however this requires us to completely re-implement the __init__ method of MockFixture, and removes the "hide traceback" functionality (though we don't care too much about this).
This allows us to have both mocker and async_mocker fixtures.

TLDR
To ease this usecase (and potentially others), Can support for defining a mock modules path from the config be added please?

@asvetlov
Copy link

Please note: asynctest.mock.CoroutineMock is not compatible with unittest.mock.AsyncMock.
It is not a drop-in replacement.
Moreover, AsyncMock doesn't exist on it's own, it is deeply integrated with other unittest.mock objects and functions. Thus, the whole unittest.mock should be replaced with asynctest.mock with even more incompatibility level.

I rather suggest not touching pytest-mock library but wait for 3.8.
Similar but slightly different API makes more harm than help.
The support of such solutions is hard in long term perspective.

If you really need asynctest.mock.CoroutineMock support you can clone pytest-mock, add needed hacks, and use it on your own.

@nicoddemus
Copy link
Member

I didn't take a look at the problem in detail, but @asvetlov makes a point about supporting slightly different APIs in the same package brings more problems than it helps.

Perhaps we should create a new pytest-asyncmock plugin instead?

@antdking
Copy link
Author

agree with your points on this.
I think it's probably best to wait until 3.8 for this library.

In terms of creating a new plugin, it might be better to try and get the fixtures into the asynctest library instead.
Will close this issue for now.

The hack we have in place works well enough for us until that time, and for our specific usecase, it will be an easy migration.

If anyone else stumbles on this issue, here is the hack:

import pytest

import pytest_mock
import asynctest.mock


@pytest.fixture
def async_mocker(pytestconfig):
    # This is a straight copy + paste from pytest_mock, but with our patched MockFixture
    result = AsyncMockFixture(pytestconfig)
    yield result
    result.stopall()


class AsyncMockFixture(pytest_mock.MockFixture):
    def __init__(self, config):
        # This is a straight copy + paste from pytest_mock
        # TODO: contribute a way to use arbitary mock libraries upstream

        self._patches = []  # list of mock._patch objects
        self._mocks = []  # list of MagicMock objects

        # CHANGED: hard coding the asynctest.mock
        self.mock_module = mock_module = asynctest.mock

        self.patch = self._Patcher(self._patches, self._mocks, mock_module)
        # aliases for convenience
        self.Mock = mock_module.Mock
        self.MagicMock = mock_module.MagicMock
        self.NonCallableMock = mock_module.NonCallableMock
        self.PropertyMock = mock_module.PropertyMock
        self.call = mock_module.call
        self.ANY = mock_module.ANY
        self.DEFAULT = mock_module.DEFAULT
        self.create_autospec = mock_module.create_autospec
        self.sentinel = mock_module.sentinel
        self.mock_open = mock_module.mock_open

        # CoroutineMock is from asynctest
        # AsyncMock is being added in python 3.8
        # Please use AsyncMock.
        self.CoroutineMock = self.AsyncMock = mock_module.CoroutineMock

@Jackenmen
Copy link

Python 3.8 is out. It might be worth reopening this issue as there's AsyncMock available now.

@nicoddemus
Copy link
Member

Thanks for the ping @jack1142.

Will gladly accept PRs in this regard if all it is required is to provide a custom mock implementation, but I probably would not like to include it into pytest-mock it this would require an entire new implementation of pytest_mock.MockFixture.

@nicoddemus nicoddemus reopened this Oct 24, 2019
@ThibaultLemaire
Copy link

ThibaultLemaire commented May 15, 2020

mock since version 4.0.0 has the AsyncMock class which could be exposed directly through mocker.AsyncMock (like the usual mocker.Mock)

EDIT: Sorry I thought pytest-mock had a dependency on mock, but this is not the case so it's not as easy as I thought.

@tirkarthi
Copy link
Contributor

FWIW, AsyncMock is now available as part of mocker from 449d3d0 with rich assert diff for assert helpers associated with AsyncMock

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants