derwiki-adroll / mock

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

Subclassing Mock uses subclass for attributes #105

Closed GoogleCodeExporter closed 9 years ago

GoogleCodeExporter commented 9 years ago
What steps will reproduce the problem?
1.
  class Foo(Mock):
    pass
2.
  f = Foo()
  print type(f.bar.baz)

What is the expected output? What do you see instead?

expected:
  <class 'Mock'>

instead:
  <class 'mock.Foo'> 
(interesting aside - the class name isn't even correct!)

What version of the product are you using? On what operating system?

>>> print mock.__version__
0.7.2

Please provide any additional information below.

This is particularly problematic when you want to add some functionality to a 
mock, but that functionality is not appropriate (and causes errors) when 
applied to its attributes.  In my case, I'm defining an adapter 
(http://twistedmatrix.com/documents/11.0.0/api/twisted.python.components.html) 
for my subclass, but *properties* of its instances should not be adaptable.

Original issue reported on code.google.com by djmitche on 18 Jul 2011 at 12:44

GoogleCodeExporter commented 9 years ago
Interesting. 

Ensuring that mock attributes are of the same type as the parent was a 
deliberate decision. If you create a subclass of Mock with helper methods then 
having those available on all attributes is very useful. This is an unfortunate 
side effect of that decision.

I'll see if I can find a way to get the best of both worlds (probably a method 
for subclasses to override to prevent attributes using the parent class).

Original comment by fuzzyman on 18 Jul 2011 at 12:12

GoogleCodeExporter commented 9 years ago
And as a note the name of the class is not incorrect. Working out why is left 
as an exercise for the reader... :-)

It's likely that any fix for this issue will be in 0.8 rather than a new 0.7 
release, because I'll probably fix it with a new feature rather than reverting 
the attribute / subclass interaction.

Original comment by fuzzyman on 18 Jul 2011 at 12:14

GoogleCodeExporter commented 9 years ago
Actually, you can already achieve this by overriding `_get_child_mock`:

{{{

class Subclass(Mock):
    def _get_child_mock(self, **kwargs):
        return MagicMock(**kwargs)

{{{

This will be used for return value and attributes. I'll add a test for this so 
that I don't break it in the future and add it as an example to the docs.

Original comment by fuzzyman on 18 Jul 2011 at 3:30

GoogleCodeExporter commented 9 years ago
That's what I've done (overriding _get_child_mock), but that leading underscore 
suggests it's not the best solution.  Maybe just making that method public (or 
renaming) would fix this?

Original comment by djmitche on 18 Jul 2011 at 4:47

GoogleCodeExporter commented 9 years ago
It's not a straightforward situation though - making it a public api would 
imply you could *call it* directly. Which you shouldn't. I think documenting it 
as the right way to solve this particular problem is the best approach.

See:

http://www.voidspace.org.uk/python/weblog/arch_d7_2011_07_16.shtml#e1221

This will be in the mock examples page from the 0.8 release.

For what its worth this is an api issue I've come across several times before - 
how to make it clear that a method is not part of the *external public api* but 
can be overridden by subclasses.

Original comment by fuzzyman on 18 Jul 2011 at 4:55