12

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

def outer():
    def inner():
        pass

outer.inner()

这导致错误消息:

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

我有没有办法针对这些嵌套函数编写单元测试?如果没有,有没有办法像类变量一样通过在它们前面加上__来触发函数名称的名称转换?

4

7 回答 7

17

内部不存在,直到外部制造它。您应该将内部移动到可测试性的顶层函数,或者让外部测试测试自身和内部的所有可能执行路径。

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

def outer(a):
    b = compute_something_from(a)
    def inner():
        do_something_with(a, b)

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

于 2008-11-28T23:44:55.140 回答
4

Python 约定是使用前导下划线命名“私有”函数和方法。当您看到前导下划线时,您就知道不要尝试使用它。

请记住,Python 不是 Java

于 2008-11-29T00:04:42.633 回答
3

我认为没有任何机会从 extern 命名空间访问 inner() 。

然而,在我看来,你保持 inner() 嵌套的事实意味着唯一真正重要的“合同”是 outer() 的。inner() 是实现的一部分,您不应该测试实现。如果您真的想测试 inner(),请使用涉及 inner() 的所有功能的数据对 outer() 进行广泛的测试。

于 2008-11-29T00:25:40.050 回答
3

我也有同样的疑问,并找到了一种方法来测试内部函数。

def outer():
    def inner():
        pass

    if __debug__:
        test_inner(inner)
        # return

def test_inner(f):
     f() # this calls the inner function

outer()

基本上,您可以将内部函数作为参数发送到外部并根据需要对其进行测试。当调用outer()时,您的测试将运行,并且由于它是一个闭包,它将保留外部函数的任何额外属性(如变量)。使用列表,您可以发送任意数量的函数。要忽略if,一个选项是运行这样的代码:

python -O code.py
于 2016-06-18T12:43:07.653 回答
3

我写了一个小的帮助模块,它允许这样做:

嵌套函数示例:

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()

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

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))

小助手模块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))
于 2016-11-09T17:08:25.697 回答
2

无法从外部函数对象获取内部函数(参见其他回复!)。然而,单元测试和闭包(至少对我而言)都为开发人员带来了惊人的性能提升。我们可以两者兼得吗?我们可以单独测试嵌套函数吗?

不容易。

然而,这似乎可以通过使用 python 模块解析器、ast 或标记器来实现代码本身,提取内部函数(通过嵌套的某些路径),并允许测试以封闭函数的状态运行它们(值用于封闭的名称)和用于更多嵌套函数的存根/模拟(在测试目标中定义)。

有人知道这样的事情吗?谷歌搜索未能找到任何东西。

于 2011-07-09T16:12:27.737 回答
0

我遇到了一个有趣的案例,我能够使用 MagicMock 对嵌套函数运行单元测试。关键是嵌套函数作为参数发送给我可以访问的另一个函数。为了保持OP的例子:

# my_app.py

def util(func):
    # run some logic using func

def outer():
    def inner():
        pass

    util(inner)

能够模拟util函数允许访问调用参数,在这种情况下恰好是inner函数:

def test_inner
    # Arrange
    mock_util = MagicMock()

    with patch.object(my_app, 'util', mock_util):
        outer() # run the outer function to capture the util call with MagicMock

    inner_function = mock_util.call_args[0][0]

    # Act
    inner_function() # run the inner function for testing

    # Assert
    # make whatever assertions you would like

这次我很幸运,因为我不需要更改我被要求测试的代码。@Victor-Barroso 的回答为那些正在寻找选择的人提供了一种有意的方式来做类似的事情。虽然,如果您要更改代码,您也可以遵循@Dustin 的建议或关于此问题的任何其他好的答案。

于 2021-05-07T21:12:49.277 回答