对嵌套函数运行单元测试

Chr*_*ieb 10 python testing closures

我来自Java世界,您可以在其中隐藏变量和函数,然后使用反射对它们运行单元测试.我使用嵌套函数来隐藏类的实现细节,以便只显示公共API.我正在尝试针对这些嵌套函数编写单元测试,以确保在开发时不会破坏它们.我试过调用一个嵌套函数,如:

def outer():
    def inner():
        pass

outer.inner()
Run Code Online (Sandbox Code Playgroud)

这会导致错误消息:

AttributeError:'function'对象没有属性'inner'

有没有办法让我对这些嵌套函数编写单元测试?如果没有,有没有办法通过为__添加前缀来触发名称,为类变量触发函数名称?

Dus*_*tin 14

内在不存在,直到外在它.您应该将内部向上移动到顶层函数以实现可测试性,或者让外部测试测试其自身和内部的所有可能的执行路径.

请注意,内部函数不是一个简单的函数,它是一个闭包.考虑这种情况:

def outer(a):
    b = compute_something_from(a)
    def inner():
        do_something_with(a, b)
Run Code Online (Sandbox Code Playgroud)

这是标准的可测试性权衡.如果你的圈复杂度太高,你的测试将会太多.


ori*_*rip 6

Python惯例是使用前导下划线命名"私有"函数和方法.当您看到前导下划线时,您知道不要尝试使用它.

请记住,Python不是Java.

  • 这是如何回答这个问题的?有没有很好的解决方案来隔离这些内部功能进行单元测试?即使没有从外部函数对象访问内部,这似乎可以通过使用python模块解析器,ast或tokenizer来实现代码本身.显然,比编码更容易陈述.:-) (17认同)
  • 它回答了这个问题,因为人们不会*测试*私有的实现细节。您可以依靠测试覆盖工具来判断您对公共接口的测试是否充分运用了私有实现。 (2认同)

Alf*_*lfe 6

我编写了一个小帮助模块,它可以实现这一点:

嵌套函数的示例:

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()
Run Code Online (Sandbox Code Playgroud)

这些可以使用这种代码进行单元测试:

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))
Run Code Online (Sandbox Code Playgroud)

小帮助模块nested如下所示:

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))
Run Code Online (Sandbox Code Playgroud)