Python allows the declaration of nested functions. These are typically hard to unit test because using just the normal ways of calling they cannot be called from outside their surrounding function. So they cannot be considered a clearly separated unit and thus cannot be unit tested.
This is a drawback of using them, so many developers (especially the ones deep into test driven development who strive to have a high unit test coverage) tend to avoid them in favor for standalone functions which can be called from the unit tests without any hassle.
But not all solutions with nested functions can be written as elegant with standalone functions. Nested functions are powerful insofar that they can access the local variables of the surrounding function without any need to pass them into the nested function, thus the code can in many cases stay neat and tidy while using a standalone function instead might raise the need to pass the complete context in form of a bunch of parameters. Also, using nested functions makes their local usage clear to any reader and keeps the name space tight.
But at least in the standard CPython (i. e. not necessarily in Jython, etc.) the implementation of functions (and methods) allows to find the nested function's code, wrap it properly to give it its needed context and then call it from the outside. I wrote a small module which helps doing exactly this.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
import types def freeVar(val): def nested(): return val return nested.__closure__ codeAttribute = '__code__' if sys.version_info == 3 else 'func_code' def nested(outer, innerName, **freeVars): if isinstance(outer, (types.FunctionType, types.MethodType)): outer = outer.__getattribute__(codeAttribute) for const in outer.co_consts: if isinstance(const, types.CodeType) and const.co_name == innerName: return types.FunctionType(const, globals(), None, None, tuple( freeVar(freeVars[name]) for name in const.co_freevars))
This module above (named
nested.py) can be used to test code like this:
#!/usr/bin/env python # version of Python can be 2 or 3, works in both cases def f(v1): v2 = 1 def g(v3=2): return v1 + v2 + v3 + 4 def h(): return 16 return g() + h() + 32 class C(object): def foo(self): def k(x): return [ self, x ] return k(3) def m(): vm = 1 def n(an=2): vn = 4 def o(ao=8): vo = 16 return vm + an + vn + ao + vo return o() return n()
The testing code then could look like this:
import unittest from nested import nested class TestNested(unittest.TestCase): def runTest(self): nestedG = nested(f, 'g', v1=8, v2=1) self.assertEqual(nestedG(2), 15) nestedH = nested(f, 'h') self.assertEqual(nestedH(), 16) nestedK = nested(C.foo, 'k', self='mock') self.assertEqual(nestedK(5), [ 'mock', 5 ]) nestedN = nested(m, 'n', vm=1) nestedO = nested(nestedN, 'o', vm=1, an=2, vn=4) self.assertEqual(nestedO(8), 31) def main(argv): unittest.main() if __name__ == '__main__': import sys sys.exit(main(sys.argv))