[TIP] I don't get it?

WW teetee at gmail.com
Mon Nov 15 07:24:49 PST 2010


Thanks for your prompt attention to this.

I have actually noticed some interesting usage patterns when utilizing mock;
it seems like there are two types of functionality I am trying to mock, the
first is where a module acts as a wrapper for a class or module which
performs some sort of external work.  In this case I find myself calling
assert_called_with quite a bit because I want the external module to have
been directed to do something specific at the completion of the test;
failing to do so constitutes a test failure.  The second type is when
another module acts as a set of utilities for the class I'm testing.  I
still want to make sure that the module is called correctly (i.e. not in a
way that will cause an error at runtime), but the class being tested has
more autonomy to decide which functions are called and how in order to get
done whatever it needs to be doing. Specifying all the function calls
exactly here makes the test too restrictive and will cause the test code to
need to be re-written when something trivial about the class being tested
changes.

The second scenario is where I envisioned mocksignature=True coming in
handy, but it's still not 100% where I want it to be, seemingly due to the
limitations of python.  I like python a lot but one has to accept that it's
simply a "dangerous" language in which it's hard to make guarantees about
whether or not code will work properly, even if one is willing to spend a
lot of time writing tests.

   -W.W.


On Fri, Nov 12, 2010 at 7:26 AM, Michael Foord <fuzzyman at voidspace.org.uk>wrote:

>  On 02/11/2010 17:02, WW wrote:
>
> You may have overlooked this from my earlier e-mail:
>
> Here's a small test case that produces the error in Python 2.6, CentOS 5.5:
>
>
> I finally found time to track this down. The cause is due to the way
> attributes are looked up (you're patching an *instance method* and methods
> don't actually live on instances they are fetched from the class when the
> method is looked up).
>
> Anyway, it's now fixed in the repository.
>
> All the best,
>
> Michael
>
>
>
>    1. from mock import patch
>     2.
>     3. class tc(object):
>     4.     def meth(a, b, c):
>     5.         pass
>     6.
>     7. g = tc()
>     8.
>     9. @patch.object(g, 'meth', mocksignature=True)
>     10. def test_g(patched):
>     11.     g.meth(1)
>     12.
>     13. test_g()
>
>
>
> On Tue, Nov 2, 2010 at 1:00 PM, Michael Foord <fuzzyman at voidspace.org.uk>wrote:
>
>>  On 02/11/2010 16:57, WW wrote:
>>
>> Replacing "@patch.object(g, 'meth', mocksignature=True)" with
>> "@patch('%s.g.meth' % __name__, mocksignature=True)" produces the same
>> error.  Am I using this wrong?
>>
>>
>>  Is this the same traceback you posted earlier?
>>
>>
>>     'SentinelObject' object has no attribute '__call__'
>>
>>  Can you reproduce this with a minimal example? I've never seen that
>> particular issue before.
>>
>>
>>
>> Thanks for the clarification regarding spec vs mocksignature.  So if I
>> wanted to do both (a mock class that raises attribute errors when invalid
>> attributes are accessed, but actual method attributes return methods that
>> enforce the correct number of parameters), do I have to set that up
>> manually, or does spec use mocksignature internally when returning attribute
>> values?
>>
>>
>>  Using mocksignature changes the way that you access the mock. As the
>> methods are replaced with 'real function objects' not mock objects as they
>> normally are, you have to do it explicitly. You could create a utility
>> function to auto patch out a mock with mocked-signatures.
>>
>> spec is purely about checking attribute access is valid.
>>
>>
>> All the best,
>>
>> Michael Foord
>>
>> Thanks again...
>>
>> On Tue, Nov 2, 2010 at 12:49 PM, Michael Foord <fuzzyman at voidspace.org.uk
>> > wrote:
>>
>>>  On 02/11/2010 16:43, WW wrote:
>>>
>>> On Tue, Nov 2, 2010 at 12:17 PM, Michael Foord <
>>> fuzzyman at voidspace.org.uk> wrote:
>>>
>>>>  On 02/11/2010 15:34, WW wrote:
>>>>
>>>> Hello,
>>>>
>>>> This is my first time using the python mock library and I'm a little
>>>> confused.  I'd like to provide some guarantees that my mocks are being
>>>> called with the correct number of arguments.  The documentation seems to
>>>> indicate there are two ways to do this, "spec" and "mocksignature", but it's
>>>> a little unclear to me what the difference is supposed to be between them.
>>>>
>>>> I find myself using the @patch.object decorator almost all the time,
>>>> because the modules I'm testing use a lot of top-level functions from
>>>> modules they've imported.  When I do something like this:
>>>>
>>>> @patch.object(somemodule, 'somemethod', spec=True)
>>>>
>>>>
>>>>  You should still be able to use patch with a named function (as a
>>>> string). See the other replies for an example.
>>>>
>>>
>>> Does this work if I'm trying to patch a global variable rather than a
>>> function in another module?
>>>
>>>
>>>  You mean a global variable in the current module? If so then still yes.
>>>
>>> @patch('%s.function' % __name__, mocksignature=True)
>>> def test_something(self, mockfunction):
>>>     ...
>>>
>>>
>>>
>>>
>>>    It doesn't seem to have any effect; I can call somemodule.somemethod
>>>> with any combination of invalid arguments and no exceptions are thrown.
>>>>
>>>>
>>>>  Using spec doesn't protect you against being called with invalid
>>>> arguments. You should get an error when you validate that the calls were
>>>> made correctly when you call 'assert_called_with'.
>>>>
>>>>
>>> assert_called_with is fine if I know the exact values I want the function
>>> to be called with, but I'm just trying to make sure that a function was
>>> called with the correct number of arguments.  If spec can't do this, then
>>> what is spec supposed to be used for?  I'm still unclear as to what the
>>> difference is between spec and mocksignature and why both exist.
>>>
>>>
>>>  spec is not for mocking functions (mocksignature is). Spec is for
>>> mocking out classes / objects to check that only methods / attributes that
>>> exist on the spec object are used. Accessing other attributes will raise an
>>> AttributeError.
>>>
>>>
>>> All the best,
>>>
>>> Michael Foord
>>>
>>>      However, when I do:
>>>>
>>>> @patch.object(somemodule, 'somemethod', mocksignature=True)
>>>>
>>>> I get:
>>>>
>>>> Traceback (most recent call last):
>>>>   File "/usr/lib/python2.6/site-
>>>> packages/mock-0.7.0b3-py2.6.egg/mock.py", line 485, in patched
>>>>     arg = patching.__enter__()
>>>>   File
>>>> "/usr/lib/python2.6/site-packages/mock-0.7.0b3-py2.6.egg/mock.py", line 536,
>>>> in __enter__
>>>>     new_attr = mocksignature(original, new)
>>>>   File
>>>> "/usr/lib/python2.6/site-packages/mock-0.7.0b3-py2.6.egg/mock.py", line 140,
>>>> in mocksignature
>>>>     signature, func = _getsignature(func, skipfirst)
>>>>   File
>>>> "/usr/lib/python2.6/site-packages/mock-0.7.0b3-py2.6.egg/mock.py", line 87,
>>>> in _getsignature
>>>>     func = func.__call__
>>>> AttributeError: 'SentinelObject' object has no attribute '__call__'
>>>>
>>>>
>>>>  This is weird. The traceback implies that you are trying to replace a
>>>> sentinel object using mocksignature (and sentinels don't have signatures to
>>>> mock). Either that or it is a bug. I'll create a simple test case here (but
>>>> this functionality *is* tested), but it looks like something is not quite
>>>> setup how you expect.
>>>>
>>>
>>> Here's a small test case that produces the error in Python 2.6, CentOS
>>> 5.5:
>>>
>>>
>>>    1. from mock import patch
>>>     2.
>>>     3. class tc(object):
>>>     4.     def meth(a, b, c):
>>>     5.         pass
>>>     6.
>>>     7. g = tc()
>>>     8.
>>>     9. @patch.object(g, 'meth', mocksignature=True)
>>>     10. def test_g(patched):
>>>     11.     g.meth(1)
>>>     12.
>>>     13. test_g()
>>>
>>>
>>>
>>>> All the best,
>>>>
>>>> Michael Foord
>>>>
>>>
>>> Likewise.
>>>
>>>>   What am I missing here?
>>>>
>>>> Thanks for your help.
>>>>
>>>>
>>>> _______________________________________________
>>>> testing-in-python mailing list
>>>> testing-in-python at lists.idyll.orghttp://lists.idyll.org/listinfo/testing-in-python
>>>>
>>>>
>>>>
>>>> -- http://www.voidspace.org.uk/
>>>>
>>>>
>>>
>>>
>>>  --
>>> http://www.voidspace.org.uk/
>>>
>>>
>>> READ CAREFULLY. By accepting and reading this email you agree,
>>> on behalf of your employer, to release me from all obligations
>>> and waivers arising from any and all NON-NEGOTIATED agreements,
>>> licenses, terms-of-service, shrinkwrap, clickwrap, browsewrap,
>>> confidentiality, non-disclosure, non-compete and acceptable use
>>> policies (”BOGUS AGREEMENTS”) that I have entered into with your
>>> employer, its partners, licensors, agents and assigns, in
>>> perpetuity, without prejudice to my ongoing rights and privileges.
>>> You further represent that you have the authority to release me
>>> from any BOGUS AGREEMENTS on behalf of your employer.
>>>
>>>
>>
>>
>> --
>> http://www.voidspace.org.uk/
>>
>> READ CAREFULLY. By accepting and reading this email you agree,
>> on behalf of your employer, to release me from all obligations
>> and waivers arising from any and all NON-NEGOTIATED agreements,
>> licenses, terms-of-service, shrinkwrap, clickwrap, browsewrap,
>> confidentiality, non-disclosure, non-compete and acceptable use
>> policies (”BOGUS AGREEMENTS”) that I have entered into with your
>> employer, its partners, licensors, agents and assigns, in
>> perpetuity, without prejudice to my ongoing rights and privileges.
>> You further represent that you have the authority to release me
>> from any BOGUS AGREEMENTS on behalf of your employer.
>>
>>
>
>
> --
> http://www.voidspace.org.uk/
>
> READ CAREFULLY. By accepting and reading this email you agree,
> on behalf of your employer, to release me from all obligations
> and waivers arising from any and all NON-NEGOTIATED agreements,
> licenses, terms-of-service, shrinkwrap, clickwrap, browsewrap,
> confidentiality, non-disclosure, non-compete and acceptable use
> policies (”BOGUS AGREEMENTS”) that I have entered into with your
> employer, its partners, licensors, agents and assigns, in
> perpetuity, without prejudice to my ongoing rights and privileges.
> You further represent that you have the authority to release me
> from any BOGUS AGREEMENTS on behalf of your employer.
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.idyll.org/pipermail/testing-in-python/attachments/20101115/16a5181f/attachment-0001.html>


More information about the testing-in-python mailing list