derwiki-adroll / mock

Automatically exported from code.google.com/p/mock
BSD 2-Clause "Simplified" License
0 stars 0 forks source link

Better instance mocking without creating real instances #136

Closed GoogleCodeExporter closed 9 years ago

GoogleCodeExporter commented 9 years ago
Hi!

Right now the only way to create isinstance-acceptable mock is to first create 
an instance of object, and then just put it into spec=obj of MagicMock, which 
is so unusable.

So basically what I want is:

Possibility to create MagicMock that would pass isinstance(mock, A) (like mock 
to be object of A()) having only A and not A() instance.

Thanks.

Original issue reported on code.google.com by k...@k-bx.com on 3 Mar 2012 at 1:38

GoogleCodeExporter commented 9 years ago
It's a good idea to combine solution with 
http://code.google.com/p/mock/issues/detail?id=137 and name it "class_spec".

Original comment by k...@k-bx.com on 3 Mar 2012 at 1:45

GoogleCodeExporter commented 9 years ago
This is just a bug, it *should* work - and in fact I thought I'd fixed this in 
0.8! A fix will be in 0.8.1.

>>> from mock import MagicMock
>>> class A(object): pass
... 
>>> m = MagicMock(spec=A)
>>> isinstance(m(), A)
False   # --> should return True

Original comment by fuzzyman on 3 Mar 2012 at 11:42

GoogleCodeExporter commented 9 years ago
I came to fix this and realised it isn't a bug but by design.

The idea is that you *should* be able to mock an instance of "A" without 
creating an instance - you should able to use "A" as a spec. That means if you 
create a mock with "A" as a spec you may intend this to be a mock instance (so 
the return value could be anything) or you may intend this to be a mock class 
(so the return value is an instance). Mock won't guess for you - you have to be 
explicit:
{{{
>>> from mock import MagicMock
>>> class A(object): pass
... 
>>> m = MagicMock(spec=A, return_value=MagicMock(spec=A))
>>> isinstance(m, A)
True
>>> isinstance(m(), A)
True
}}}

There is one place where mock *will* guess for you, and this is where you are 
using patch to replace a class and you set "spec=True". If the object being 
replaced is a class then mock *knows* you are replacing a class, so it will set 
the spec on the return value mock for you:
{{{
>>> from mock import patch
>>> class Foo(object): pass
... 
>>> FooClass = Foo
>>> with patch('__main__.FooClass', spec=True) as M:
...   print isinstance(M, Foo)
...   print isinstance(M(), Foo)
... 
True
True
}}}

Original comment by fuzzyman on 8 Mar 2012 at 6:58

GoogleCodeExporter commented 9 years ago
Ok, so here you will create an object with spec of class, that doesn't work in 
real world (where in __init__ you will do self.a = 'foo'.

Should I create a separate bug report for "going through isinstance without 
limiting to spec"? Thanks.

Original comment by k...@k-bx.com on 12 Mar 2012 at 11:09

GoogleCodeExporter commented 9 years ago
The documentation talks about the problems with using a class for a spec where 
you have instance attributes that aren't on the class. One of the ways to  
solve this is to use class attributes as defaults. Another one is to "set" 
instance attributes manually on the mock after creation (perhaps via a helper 
function if you have to do it a bunch of times). As a third option you can use 
a list of strings as your spec instead of the class object.

If you loosen spec so that it allows you to access any attribute then what is 
the point of spec now? (You might as well just use an ordinary mock - spec 
*only* limits the available attributes.)

Original comment by fuzzyman on 12 Mar 2012 at 6:29

GoogleCodeExporter commented 9 years ago
> If you loosen spec so that it allows you to access any attribute then what is 
the point of spec now?

That's my point too! I don't need spec, I just want isinstance to pass.

Original comment by k...@k-bx.com on 14 Mar 2012 at 5:13