I have an entry point function call it main
on an object that I would like to remain unmocked, since it calls several other methods on the object:
class Thing(object):
def main(self):
self.alpha()
self.bravo()
def alpha(self):
self.charlie()
def bravo(self):
raise TypeError("Requires Internet connection!")
def charlie(self):
raise Exception("Bad stuff happens here!")
This is pretty straight forward to mock manually:
thing = Thing()
thing.alpha = MagicMock()
thing.bravo = MagicMock()
And I can test to make sure that alpha and bravo are both called once, I can set side effects in alpha and bravo to make sure they're handled, etc. etc.
What I'm worried about is if the code definition changes and someone adds a charlie
call to main
. It's not mocked, so now the side effects will be felt (and they're things like writing to a file, connecting to a database, fetching stuff from the Internet, so that simple exception is not going to alert me that the tests are now bad).
My plan was to verify that my mock object calls no other methods than the ones I say it should (or raise a test exception). However if I do something like this:
MockThing = create_autospec(Thing)
thing = Thing()
thing.main()
print thing.method_calls
# [calls.main()]
Then main
is also mocked so it calls no other methods. How can I mock every method but the main method? (I'd like method_calls to be [calls.alpha(), calls.bravo()]
).
Edit: For Hacking Answers
Well, I have a really hacky solution, but I hope there is a better answer than this. Basically I rebind the method from the original class (Python bind an unbound method)
MockThing = create_autospec(Thing)
thing = MockThing()
thing.main = Thing.ingest.__get__(thing, Thing)
thing.main()
print thing.method_calls
# [calls.alpha(), calls.bravo()]
But there has to be a simpler solution than using function descriptors!