Stubbing vs. Mocking ##################### We've done a lot of testing of Python systems over the past couple of years. The best approach we've found so far for replacing the behaviour of test objects at runtime is stubbing. We've read a lot about mocking and even written mocking libraries for other languages. But there is something about the way that mocks need to be manipulated that feels distinctly un-Pythonic. Unlike some other languages, Python allows you to access most of the internals of objects easily at runtime. Using this facility, we have developed the :mod:`stub` library to help with replacing functionality on functions and objects. Separation of concerns ======================= When you can modify objects and functions for testing purposes and confine those modifications to testing code, your production code becomes much cleaner and easier to read. You can do away with all the large ``if debug:`` blocks and keep everything clear and concise. This problem is usually solved with complex type hierarchies providing levels of abstraction that allow developers to plug testing implementations into the pre-defined interface. What's funny/sad about this is that it is pure duplication of effort. We already have an interface, it's just that we need to hook into it. Most of the function definitions and current type hierarchies do not need to be further abstracted in order to support testing. A prime example is database connector modules. Obviously, you expect this module to actually connect to a database. Equally obvious is the fact that you don't want this module connecting to a database for every single one of it's thousands of unit-tests. So most projects have an abstraction layer with an ``AbstractAccessor`` superclass that is then subclassed into ``MySQLAccessor`` and ``TestAccessor`` classes. This is not a terrible solution, but it *does* increase the cognitive overhead of dealing with the hot, or most common, path of execution. When we write code that is clear in intention, it is easy to maintain and update. Each successive layer of abstraction we add makes this more difficult. So keeping the test code *completely* separate from the production code just makes sense. This philosophy leads us to prefer code like this:: class MySQLAccessor: def __init__(self): pass def connect(self, user=None, pass=None, dbhost=None): do_something() def myfunc(x): return do_something_real() # module mylib_test import stub def myfunc_repl(x): return 5 + x stub.stub(myfunc, myfunc_repl) But while stubbing may seem easy when you look at replacing behaviour on instance objects, stubbing functional code turns out to be a whole 'nother ball of wax... But I *did* import it! ======================= One of the most frustrating things when testing is needing to replace the behaviour of a function from another module. When the function is used in multiple places in the codebase and each place uses it's own ``import`` statement, each namespace has a binding to the original function so simple monkeypatching of the source module won't help:: # Module module_a import mylib do_something(mylib.myfunc()) # Module module_b import mylib do_something_else(mylib.myfunc()) # Module mylib def myfunc(): return do_something_we_dont_want() In this example, the naive approach to replacing the implementation of ``mylib.myfunc`` is to monkeypatch the `mylib` module like so:: # Module mod_mylib import mylib def myfunc_repl(): return do_something_desirable() mylib.myfunc = myfunc_repl Unfortunately, this code must execute *before* the code in `module_a` and `module_b` or else they will get handles on the original `myfunc` before it's definition is replaced. In order to update the code executed by all handles to the original `myfunc` it is necessary to modify the internal code object that the function object wraps. In this way, any modules that have already imported the function get the updated functionality. The `stub` library handles this properly for both unbound functions and object methods. Allowing you to replace the above monkeypatching code with the following:: # Module mod_mylib import stub import mylib def myfunc_repl(): return do_something_desirable() stub(mylib.myfunc, myfunc_repl) This code can run at any time regardless of imports into other modules and will correctly update the code of `myfunc` so that all handles execute the new instructions.