Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I stub a class in a module in Python for testing?

Tags:

python

stub

I have a module I am using which uses RealClass, so it is an internal dependency I don't have access to.

I want to be able to create a FakeClass which replaces the functionality of the RealClass for testing. I don't want to replace individual methods but the entire class.

I looked at stubble which seems to be what I want but I was wondering if mox or any of the other mocking frameworks have this functionality? Or what would you suggest to use? Maybe fudge, monkey-patching? Just looking for best practices with this stuff. Also any useful examples would be awesome.

Pseudo code:

from module import RealClass

class FakeClass
    methodsFromRealClassOverridden

class Test(unittest.TestCase):
    setup()
    teardown()

test1()
    stub(RealClass, FakeClass) // something like this, but really just want the functionality
    classThatUsesRealClass // now will use FakeClass

UPDATE:

Here's one way I found to do it. It isn't perfect but it works.

Example:

fake = FakeClass()
stub = stubout.StubOutForTesting()
stub.Set(RealClass, 'method_1', fake.method_1)
stub.Set(RealClass, 'method_2', fake.method_2)
like image 264
dre Avatar asked Feb 22 '23 20:02

dre


1 Answers

I think you want opinions/experiences so I'm just giving my 2 cents.

As you noticed there are a few Python testing tools/classes/frameworks, but most of the time given the simplicity/dynamism/openness of Python you will limit yourself to using ad-hoc relevant test cases which involve stubbing at the interface level, and a bit of unittest... until you start using the frameworks.

There is nothing pejorative about monkey-patching, especially when it comes to performing testing/stubbing:

#!/usr/bin/env python
# minimal example of library code

class Class:
    """ a class """
    def method(self, arg):
        """ a method that does real work """
        print("pouet %s" % arg)

#!/usr/bin/env python
# minimal example for stub and tests, overriding/wrapping one method
from Class import Class

Class._real_method = Class.method
def mymethod(self, arg):
    # do what you want
    print("called stub")
    # in case you want to call the real function...
    self._real_method(arg)
Class.method = mymethod

# ...

e = Class()
e.method("pouet")

Namespaces will allow you to patch stuff inside of imported modules inside of imported modules...

Note that the above method does not work with classes in C modules. For them you can use a wrapper class that filters on class member names using getattr/setattr, and returns the redefined members from the wrapper class.

#!/usr/bin/env python
# Stupid minimal example replacing the sys module
# (not very useful / optimal, it's just an example of patching)

import sys

class SysWrap():
    real = sys
    def __getattr__(self, attr):
        if attr == 'stderr':
            class StdErr():
                def write(self, txt):
                    print("[err: %s]" % txt)
            return StdErr()
        print("Getattr %s" % attr)
        return getattr(SysWrap.real, attr)

sys = SysWrap()
# use the real stdout
sys.stdout.write("pouet")
# use fake stderr
sys.stderr.write("pouet")

Once you are becoming tired of performing ad-hoc testing, you'll find higher level stuff such as the ones you mentioned (stubble, fudge) useful, but to enjoy them and use them efficiently you have to first see the problems they solve and accept all the automatic stuff they do under the hood.

It is probable that a part of ad-hoc monkey patching will remain, it's just easier to understand, and all the tools have some limitations.

Tools empower you but you have to deeply understand them to use them efficiently.

An important aspect when deciding whether to use a tool or not is that when you transmit a chunk of code, you transmit the whole environment (including testing tools). The next guy might not be as smart as you and skip the testing because your testing tool is too complex for him. Generally you want to avoid using a lot of dependencies in your software.

In the end, I think that nobody will bother you if you just use unittest and ad-hoc tests/monkey-patching, provided your stuff works. Your code might not be that complex anyway.

like image 77
cJ Zougloub Avatar answered Feb 25 '23 16:02

cJ Zougloub