【发布时间】:2015-05-06 21:38:24
【问题描述】:
如果相关的话,我正在使用 Python。
据我了解,单元测试是对与单个用例相关的最小代码部分的测试。通常这是一个单一的功能或方法。
所以当我测试我的函数时,我不想测试它正在调用的任何函数。在编写测试时,这很容易做到:只需关注函数正在执行的逻辑即可。
但是当我遇到以下情况时:
def is_prime(number):
if number <= 1:
return False
for element in range(2, number):
if number % element == 0:
return False
return True
def extract_primes(lst):
return [item for item in list if is_prime(item)]
我对@987654323@ 的测试看起来如何感到困惑。
def test_extract_primes():
lst = [0, 1, 2]
result = extract_primes_from_list(lst)
assert result == [2]
# and some more similar tests for empty lists, lists with only primes
# and lists with no primes
但现在我只是在隐式测试is_prime 是否正常工作。而当is_prime 出现错误时,也会导致extract_primes 的测试失败,这超过了单元测试的要点,您希望在其中快速发现单点故障。
所以在我看来,我一开始就不应该打电话给is_prime。所以我最终得到了这样的结果:
@unittest.mock.patch("path.to.module.is_prime")
def test_extract_primes(mock_is_prime):
mock_is_prime.return_value = True
lst = list(range(10))
result = extract_primes(lst)
assert result == lst
for i in lst:
assert mock_is_prime.called_with(i) # not sure if correct method name
但这充其量是令人厌烦且不灵活的。我不能让mock_is_prime 在同一个测试中返回不同的值,除非我创建一个类似的构造:
bool_list = [True, False, True]
mock_is_prime.side_effect = lambda x: next(bool_list)
但这会让更加冗长。随着被测函数中函数调用次数的增加,用于修补这些函数的愚蠢样板代码数量也在增加。
当我尝试在互联网上寻找答案时,我大多会得到 Java/C# 依赖注入指令,它们会告诉你传递正确的所需对象作为参数,然后为你的方法创建一个该对象的虚拟对象'重新测试。或者我会争论是否要测试私有方法。没关系,我理解那些事情。但是我一辈子都无法弄清楚如何处理依赖于其他函数调用的函数。我应该简单地注入这些函数吗?
def extract_primes(lst, is_prime_function=is_prime):
return [item for item in list if is_prime_function(item)]
然后将一个虚拟的is_prime 函数传递给正在测试的extract_primes?这看起来很愚蠢,并且在函数签名中乱扔了奇怪的参数。这实际上与在所需的工作量上修补功能没有什么不同。它只是从测试中删除了一个 @patch 语句。
所以我不应该首先修补功能吗?如果不是,那么一个函数在什么时候才值得修补?只有当它操纵系统时?或者当它来自一个完全独立的模块时?
我有点失落。
【问题讨论】:
-
这可能更适合programmers.SE。
-
另外:这并不能回答你的问题,但是 --
extract_primes = partial(filter, is_prime) -
@tzaman 我很清楚。我只需要示例代码。实际代码很难在问题中快速解释。每个人都知道素数是什么。所以我会用一个关于素数的问题。
标签: python unit-testing testing