我正在为一些激光驱动器功能创建一些测试。对于某些激光器,支持一些子功能,在其他激光器上,我应该预期会出现错误(从而测试是否出现了错误)。 子功能当然(为了保持简单;-))不是1个功能,而是多个功能。因此,给定激光器,某个功能的测试应该通过或失败。
传递大小写不是问题,但是当它失败并且函数有多个返回值时,我们创建的泛型断言有一个问题,即它不知道应该返回到测试函数的返回值的数量因此,在激光器不支持子功能的情况下,我们的通用断言仅返回1无值。
因此,如果函数失败或通过,我们必须检查具有多个返回值的函数的测试,并且只能以正确的方式调用我们的泛型断言。 但这是一种丑陋的恕我直言,因为在这种情况下,我们检查子功能是否支持两次(通用断言的目标是在一个地方做到这一点)。
有没有办法确定函数预先调用函数的返回值的数量?
伪代码中的问题:
通用断言(加上测试的基本设置):
import functionality_under_test as fut
class Functionality( unittest.TestCase ):
def shared_setup( self ):
Functionality._issupported = fut.subfunctionality.is_supported()
def _assert_sub_functionality( self, func, *args, **kwargs ):
retval = None # We don't know how many retvals there will be
if not self._issupported:
# Here is the place where it should determine how many
# return-values func really has and initialize retval as
# with the correct number of None's in a tuple / one None.
self.assert_raises( <expected error>, func, *args, **kwargs )
else:
# retval will be filled with either 1 value or a tuple by
# successful call.
retval = func( *args, **kwargs )
return retval
对于具有1个返回值的函数,没有问题,因此我们可以这样做:
def test_subfunc_func_1_retval( self ):
only_retval = self._assert_sub_functionality( subfunc_func_1_retval, <args>, <kwargs> )
<checks based on retval not being None>
但是对于具有多个返回值的测试函数,这不起作用(引发错误,因为在测试函数预期多次的情况下仅给出1次返回)。所以我们就这样做了:
def test_subfunc_func_2_retvals( self ):
if not self._issupported:
# 'Fail' case, where an error is expected
retval1, retval2 = None
_ = self._assert_sub_functionality( subfunc_func_2_retvals, <args>, <kwargs> )
else:
# Pass-case, no error expected. This works fine.
retval1, retval2 = self._assert_sub_functionality( subfunc_func_2_retvals, <args>, <kwargs> )
<checks based on the retvals not being None>
对self._issupported支持的额外检查是一种丑陋的恕我直言。
这可以用不同的方式完成吗?
_assert_sub_functionality可以确定一个函数有多少个返回值,并且在不支持子功能的情况下多次返回None?
TIA
答案 0 :(得分:4)
首先,函数只有一个返回值,但是it could be a tuple。
所以,你的问题是,你能否静态分析一个Python函数并推断它只返回特定元组,即那些具有一定大小的元组。
不幸的是,答案是否定的 - 很容易证明这会解决Halting Problem。
答案 1 :(得分:0)
看看Ami Tavory和David Ehrmann给出的答案(以及Halting Problem wiki-page ;-))我得到了以下解决方案:
def _assert_sub_functionality( self, func, *args, **kwargs ):
supported = self._issupported
retval = None
if not supported:
self.assert_raises( <expected error>, func, *args, **kwargs )
else:
# retval will be filled with either 1 value or a tuple by
# successful call.
retval = func( *args, **kwargs )
return supported, retval
def test_subfunc_func_1_retval( self ):
retval = self._assert_sub_functionality( subfunc_func_1_retval, <args>, <kwargs> )
issupported = retval[0]
if issupported:
real_retval = retval[1]
<tests with real_retval>
def test_subfunc_func_2_retvals( self ):
retval = self._assert_sub_functionality( subfunc_func_2_retvals, <args>, <kwargs> )
issupported = retval[0]
if issupported:
(real_retval1, real_retval2) = retval[1]
<checks with the real_retvals>
对两者都赞不绝口。 (解决方案检查也应该在David的评论之前;-))。