[TIP] Issue with "mock" package and isinstance()

Tom Davis tom at recursivedream.com
Fri Jul 29 10:37:48 PDT 2011


On 07/29/2011 01:29 PM, Michael Foord wrote:
> On 29/07/2011 18:03, Tom Davis wrote:
>> On 07/29/2011 12:42 PM, Michael Foord wrote:
>>> On 29/07/2011 17:31, Tom Davis wrote:
>>>> Hey all,
>>>>
>>>> I've been using Michael Foord's awesome /mock/ package and I've 
>>>> loved it... now I'm running into an issue. I'm patching a class, 
>>>> pretty simple:
>>>>
>>>>     from my.module.path import Class
>>>>     patch = mock.patch('my.module.path.Class', spec=Class)
>>>>     mocked = patch.start()
>>>>     mocked.return_value = MyReplacementClass()
>>>>
>>>> I'm doing things this way because I need to mock out a class, but 
>>>> provide an alternate implementation that uses similar logic to the 
>>>> real class (basically mocking an external API by turning it into a 
>>>> memory-mapped API).
>>>>
>>>> Somewhere else, my code gets this mock client and a function calls:
>>>>
>>>>     isinstance(client, my.module.path.Class): ...
>>>>
>>>> This call fails with:
>>>>
>>>>     TypeError: isinstance() arg 2 must be a class, type, or tuple
>>>>     of classes and type
>>>>
>>>> This was confusing to me, so I did a bit of digging to figure out 
>>>> what /Class/ really was, but all was well. Here's what I got:
>>>>
>>>>     <MagicMock spec='Class' id='50389456'>
>>>>
>>>
>>> Well yes - what you have here is a mock *instance* - it isn't a 
>>> class it's a mock of a class. So you can't use this as the second 
>>> argument to isinstance. This is one of the problems with type 
>>> checking (not that it's always wrong - just that it causes this kind 
>>> of difficulty).
>>>
>>> Actually it *would* be possible to make a mock object behave like a 
>>> class, by providing __bases__ = (type,) on mock instances (or 
>>> whatever the appropriate bases would be).
>> First off, thanks for the super fast reply! I'm trying to wrap up 
>> this project and the intricacies of the final patching stage are 
>> confusing me a bit. So if I understand this correctly, you're saying 
>> that isinstance() is actually calling (or otherwise obtaining what i 
>> set as "return_value") the mock? 
>
> Not quite, no.
>
> In your test you are replacing "my.module.path.Class" with a mock. So 
> when your code tries to use "my.module.path.Class" as the second 
> argument to isinstance, it finds the mock. This is what your patching 
> has done. You are telling patch to replace Class with a mock - so that 
> is what has happened! Unless I have misunderstood - but that's what it 
> looks like from both your code and description...
>
Quite right! However--and maybe this is where I am confused--I was under 
the impression that providing the "spec" or "spec_set" argument to Mock 
(or in this case, indirectly to Mock via patch()) allowed it to pass 
isinstance() tests.

What I *really* want is for calling Class() to return a pre-built 
replacement. I don't want Class itself to be an instance of anything, if 
I can help it. I want it to be a class that "looks" just like the 
original, except for the fact that it returns (e.g. via __new__) a 
custom subclass. I thought patching it and providing "return_value" on 
the patched result would do this. But I guess I am creating Mock 
instances instead? Maybe my "mocked" object just needs to be a sublass 
of Class with __new__ replaced? Perhaps via the "new" argument to 
patch()? I will continue to investigate.
> Michael
>
>> The result would make sense, then, though I thought it just checked 
>> its type? This is a bit above my meta-Python experience.
>>>
>>> Can you add this as a feature request:
>>>
>>> https://code.google.com/p/mock/issues/list
>>>
>> Provided I actually understand what it is (see above), I certainly will.
>>
>> Thanks again!
>>
>> -Tom
>>> All the best,
>>>
>>> Michael Foord
>>>>
>>>> Seems right to me! Additionally, if I change the instance check in 
>>>> the production code to:
>>>>
>>>>     isinstance(client, my.module.path.Class._spec_class): ...
>>>>
>>>> It works just fine! I can't do this for obvious reasons, but it 
>>>> seems to prove that the MagicMock at least has the correct 
>>>> information somewhere, it's just not being properly inspected by 
>>>> isinstance().
>>>>
>>>> Any thoughts? I really need this to work.
>>>>
>>>>
>>>> _______________________________________________
>>>> testing-in-python mailing list
>>>> testing-in-python at lists.idyll.org
>>>> http://lists.idyll.org/listinfo/testing-in-python
>>>
>>>
>>> -- 
>>> http://www.voidspace.org.uk/
>>>
>>> May you do good and not evil
>>> May you find forgiveness for yourself and forgive others
>>> May you share freely, never taking more than you give.
>>> -- the sqlite blessinghttp://www.sqlite.org/different.html
>>
>
>
> -- 
> http://www.voidspace.org.uk/
>
> May you do good and not evil
> May you find forgiveness for yourself and forgive others
> May you share freely, never taking more than you give.
> -- the sqlite blessinghttp://www.sqlite.org/different.html

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.idyll.org/pipermail/testing-in-python/attachments/20110729/7f4c3902/attachment.htm>


More information about the testing-in-python mailing list