Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to stub Python methods without Mock

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?

like image 825
Corey Coogan Avatar asked Oct 11 '10 21:10

Corey Coogan


People also ask

Is stubbing the same as mocking?

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.

How do you create a stub in Python?

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.

Should I use mock or stub?

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.

When should you not use a mock?

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.


2 Answers

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.

like image 102
ptyshevs Avatar answered Oct 22 '22 10:10

ptyshevs


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()
like image 35
Peter Lyons Avatar answered Oct 22 '22 10:10

Peter Lyons