I'm a C# dev moving into some Python stuff, so I don't know what I'm doing just yet. I've read that you don't really need Dependency Injection with Python. I've been told you can instantiate objects in your code and have them run the way you want, however, you can point methods on those objects to my own stubs defined in my tests - supposedly without mocks.
Is this true? I've tried to do it and can't quite get it working. How is this actually done? How do I stub a method in Python without a mocking library?
Stubbing, like mocking, means creating a stand-in, but a stub only mocks the behavior, but not the entire object. This is used when your implementation only interacts with a certain behavior of the object.
Create a stub file for your own implementationSelect File | New from the main menu, then select Python File (alternatively, use the Alt+Insert shortcut). In the New Python file dialog, select Python stub and specify the filename. The filename should be the same as the name of the implementation file.
Mocks should be used when you want to test the order in which functions are called. Stubs verify the state of the system under test. Stubs don't take order into account, which can be helpful for reducing the work of rewriting tests when code is refactored.
Only use a mock (or test double) “when testing things that cross the dependency inversion boundaries of the system” (per Bob Martin). If I truly need a test double, I go to the highest level in the class hierarchy diagram above that will get the job done. In other words, don't use a mock if a spy will do.
Python functions are first-class objects, so you can assign any function to the identifier:
class Person:
def greetings(self):
return "Hi!"
def happy_greetings(self):
return "Hi! You're awesome!"
mike = Person()
mike.greetings() # "Hi!"
Person.greetings = happy_greetings
mike.greetings() # "Hi! You're awesome!"
Think of method identifier as a reference to some function. By changing the reference, Python interpreter will find and execute whatever it refers to. However, instead of doing this by hand, I suggest you using some mature module, like unittest.mock, since there are plenty of pitfalls like managing stub scope, etc.
Here's a basic example. Note that the production getData() method is never called. It has been mocked out with a stub.
import unittest
class ClassIWantToTest(object):
def getData(self):
print "PRODUCTION getData called"
return "Production code that gets data from server or data file"
def getDataLength(self):
return len(self.getData())
class TestClassIWantToTest(unittest.TestCase):
def testGetDataLength(self):
def mockGetData(self):
print "MOCK getData called"
return "1234"
origGetData = ClassIWantToTest.getData
try:
ClassIWantToTest.getData = mockGetData
myObj = ClassIWantToTest()
self.assertEqual(4, myObj.getDataLength())
finally:
ClassIWantToTest.getData = origGetData
if __name__ == "__main__":
unittest.main()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With