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

Make spy a context manager #118

Open
neumond opened this issue Jun 14, 2018 · 5 comments
Open

Make spy a context manager #118

neumond opened this issue Jun 14, 2018 · 5 comments

Comments

@neumond
Copy link

neumond commented Jun 14, 2018

Capture return value

NOTE: already implemented in 1.11

class SomeClass:
    def method(self, a):
        return a * 2

    def facade(self):
        self.method(4)
        return 'Done'


def test_some_class(mocker):
    spy = mocker.spy(SomeClass, 'method')
    o = SomeClass()
    o.facade()
    assert spy.called
    assert spy.call_count == 1

    print(spy.mock_calls[0])
    # nicely outputs method arguments
    # call(<m.SomeClass object at 0x7febfed74518>, 4)

    print(spy.return_value)
    # just tells us we have mock object here
    # <MagicMock name='method()' id='140651569552856'>
    
    # it would be much better to have there
    # assert spy.return_value == 8
    # or, rather
    # assert spy.mock_calls[0].return_value == 8

Spy as context manager

That's probably kind of misusing mocker object, but in my case there's a quite long test and I want to restrict fragment of code where particular spy object is applied.

def test_context_mgr(mocker):
    o = SomeClass()
    with mocker.spy(SomeClass, 'method') as spy:  # AttributeError: __enter__ here
        o.facade()
        assert spy.call_count == 1
    o.facade()

Currently I can only find arguments

@nicoddemus
Copy link
Member

Hi @neumond,

Thanks for taking the time to write your suggestions! Also appreciate the easy to follow examples.

Unfortunately none of the suggestions can be implemented without breaking the current interface, so we might need a new method name that returns an object that:

  • can records the return value of mocked functions;
  • can be used as a context manager.

Do you have any suggestions?

cc @fogo, which contributed mocker.spy initially.

@alexAubin
Copy link

Hello there,

just found this randomly while looking for some sort of "spy as context manager" feature. Though what naively imagined would be more like :

def test_context_mgr(mocker):
    o = SomeClass()
    with mocker.spy(SomeClass, 'method', call_count=1):
        o.facade()
    o.facade()

(not necessarily using spy() as name if it's troublesome)

The motivation would be to have something similar to pytest.raises which only works for exceptions as far as I understand.

nicoddemus added a commit to nicoddemus/pytest-mock that referenced this issue Sep 28, 2019
nicoddemus added a commit to nicoddemus/pytest-mock that referenced this issue Sep 28, 2019
nicoddemus added a commit to nicoddemus/pytest-mock that referenced this issue Sep 28, 2019
nicoddemus added a commit to nicoddemus/pytest-mock that referenced this issue Sep 28, 2019
nicoddemus added a commit that referenced this issue Sep 28, 2019
@Mark90
Copy link

Mark90 commented Nov 13, 2019

I run into a similar need of capturing return values from the object spied upon. My solution was a wrapper around unittest.mock.patch.object which adds a side-effect to stores both function call args as well as function result (return value or exception raised). But then I also recreated the same yield_fixture to guarantee the patched object would be restored, so basically I've duplicated quite a bit of pytest-mock. And I hate duplication. :)

If we can agree on what the solution should roughly look like,I can try writing up a PR.
What about mocker.watch() with same signature as spy()? It would basically work like patch.object, then after starting the patch apply a side_effect wrapper that registers call args/returnvalue/exceptions (I'm storing them as namedtuples, but can change that).

@Mark90
Copy link

Mark90 commented Nov 13, 2019

Didn't notice that you already implemented this with 1.11. Please disregard my comment.

Awesome work by the way :)

@nicoddemus nicoddemus changed the title Spy improvements: capture return value, make spy a context manager Make spy a context manager Nov 18, 2019
@nicoddemus
Copy link
Member

@Mark90 indeed the issue was a bit confusing, thanks for bringing this to attention.

I've updated the title and mentioned that the return value has been implemented in 1.11. 👍

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

No branches or pull requests

4 participants