I come from the Java world, where you can hide variables and functions and then run unit tests against them using reflection. I have used nested functions to hide implementation details of my classes so that only the public API is visible. I am trying to write unit tests against these nested functions to make sure that I don't break them as I develop. I have tried calling one of the nested functions like:
def outer():
def inner():
pass
outer.inner()
which results in the error message:
AttributeError: 'function' object has no attribute 'inner'
Is there a way for me to write unit tests against these nested functions? If not, is there a way to trigger the name munging for function names like you can for class variables by prefixing them with __?
The Python convention is to name "private" functions and methods with a leading underscore. When you see a leading underscore, you know not to try and use it.
Remember, Python is not Java.
inner doesn't exist until outer makes it. You should either move inner up to a toplevel function for testability, or have the outer test test all the possible execution paths of itself and inner.
Do note that the inner function isn't a simple function, it's a closure. Consider this case:
def outer(a):
b = compute_something_from(a)
def inner():
do_something_with(a, b)
That's the standard testability trade-off. If your cyclomatic complexity is too high, your tests will be too numerous.
I don't think that there is any chance to access inner() from the extern namespace.
However, in my opinion the fact that you keep inner() nested implies that the only "contract" that really matters is outer()'s one. inner() is part of the implementation, and you shouldn't want to test the implementation.
If you really want to test inner(), do extensive tests on outer() with data that will involve all the functionalities of inner().
No way to get inner function from outer function object (see the other replies!). Yet both unit tests and closures have made (for me at least) amazing developer performance improvements. Can we have both? Can we test nested functions in isolation?
Not easily.
However, such could seemingly be achieved with use of python modules parser, ast, or tokenizer to dice up the code itself, extracting inner functions (by some path through the nesting), and allowing tests to run them with state from enclosing functions (values for closed-over names) and stubs/mocks for more-nested functions (defined within the test target).
Anybody know of anything like this? Googling failed to find anything.
I had the same doubt and found a way to get tests going for inner functions.
def outer():
def inner():
pass
if __debug__:
test_inner(inner)
# return
def test_inner(f):
f() # this calls the inner function
outer()
Basically you can send the inner function as a parameter to the outside and test it as you wish. When calling outer(), your test will run, and since it's a closure, it will preserve any extra property from the outer function (like variables). Using a list, you can send as many functions as you wish. To ignore the if, an option is to run the code like that:
python -O code.py
I have written a small helper module which allows exactly this:
Examples of nested functions:
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()
These can be unit tested using this kind of code:
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))
The small helper module nested
looks like this:
import types
def freeVar(val):
def nested():
return val
return nested.__closure__[0]
def nested(outer, innerName, **freeVars):
if isinstance(outer, (types.FunctionType, types.MethodType)):
outer = outer.func_code
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))