如何在 Python 中正确进行单元测试
How to properly unittest in Python
我有一个方法可以执行以下操作。问题是我如何对这种方法进行单元测试。我对这个 Python 单元测试模块很陌生。
问题及解决方法如下:
Given a string containing of ‘0’, ‘1’ and ‘?’ wildcard characters, generate all binary strings that can be formed by replacing each wildcard character by ‘0’ or ‘1’.
Example :
Input str = "1??0?101"
Output:
10000101
10001101
10100101
10101101
11000101
11001101
11100101
11101101
解决方案:
def _print(string, index):
if index == len(string):
print(''.join(string))
return
if string[index] == "?":
# replace '?' by '0' and recurse
string[index] = '0'
_print(string, index + 1)
# replace '?' by '1' and recurse
string[index] = '1'
_print(string, index + 1)
# NOTE: Need to backtrack as string
# is passed by reference to the
# function
string[index] = '?'
else:
_print(string, index + 1)
# Driver code
if __name__ == "__main__":
string = "1??0?101"
string = list(string) #don’t forget to convert to string
_print(string, 0)
输出:
10000101
10001101
10100101
10101101
11000101
11001101
11100101
11101101
问题:
1.另外,有没有办法将列表作为输出返回而不是打印出来?
2。哪些断言测试用例适用于这种情况?
3。在这种情况下,最好的端到端测试用例是什么?
4.在时间和 space 复杂性方面,有什么更好的方法可以解决这个问题?
我已经试过了,但似乎不起作用:
import unittest
from wildcard import _print
class TestWildCard(unittest.TestCase):
def test_0_print(self):
print("Start wildCard _print test: \n")
result = 111
self.assertEquals(_print("1?1",0),result,"Results match")
答案:
1: 当然,不是打印一些东西,而是将结果附加到列表 result.append('some value')
并且不要忘记在代码开头初始化列表 result = []
和 return 一旦函数完成 return result
- 并且可能不会调用函数 _print
,而是像 bit_strings
.
ad 1: 由于你的函数是递归的,你现在还需要捕获 return 值并在递归调用函数时将其添加到结果中,所以 result += _print(string, index + 1)
2:您通常应该考虑边缘情况并分别对其进行测试,或者将它们组合在一起以真正测试您的功能的单个方面。没有一种方法可以说明测试应该是什么样子 - 如果有,测试框架会为您生成它。
3:与 2 相同的答案。
您的代码变为:
def bit_strings(s, index):
result = []
if index == len(s):
result.append(''.join(s))
return result
if s[index] == "?":
# replace '?' by '0' and recurse
s[index] = '0'
result += bit_strings(s, index + 1)
# replace '?' by '1' and recurse
s[index] = '1'
result += bit_strings(s, index + 1)
# NOTE: Need to backtrack as string
# is passed by reference to the
# function
s[index] = '?'
else:
result += bit_strings(s, index + 1)
return result
# Driver code
if __name__ == "__main__":
x = "1??0?101"
xl = list(x) #don’t forget to convert to string
print(bit_strings(xl, 0))
有更有效的方法可以做到这一点,但我只是根据问题和答案修改了您的代码。
我已将 string
重命名为 s
,因为 string
有点令人困惑,提醒其他人该类型或隐藏(内置)模块。
至于单元测试:
import unittest
from wildcard import bit_strings
class TestWildCard(unittest.TestCase):
def test_0_print(self):
print("Start wildCard _print test: \n")
# you only had one case here and it's a list now
result = ['101', '111']
# user assertEqual, not Equals
# you were passing in a string, but your code assumed a list, so list() added
self.assertEqual(bit_strings(list("1?1"), 0), result, "Results match")
当使用像 PyCharm 这样的环境时,它有助于调用文件 test<something>.py
(即名称中有 test
),这样它可以帮助您 运行单元测试更容易。
评论中要求的两个替代解决方案(一个仍然是递归的,只是更简洁,另一个不是递归的,但可以说结果列表有点浪费 - 只是两个快速):
from timeit import timeit
def unblank_bits(bits):
if not bits:
yield ''
else:
for ch in '01' if bits[0] == '?' else bits[0]:
for continuation in unblank_bits(bits[1:]):
yield ch + continuation
print(list(unblank_bits('0??100?1')))
def unblank_bits_non_recursive(bits):
result = ['']
for ch in bits:
if ch == '?':
result = [s+'0' for s in result] + [s+'1' for s in result]
else:
result = [s+ch for s in result]
return result
print(list(unblank_bits_non_recursive('0??100?1')))
print(timeit(lambda: list(unblank_bits('0??100?1'))))
print(timeit(lambda: list(unblank_bits_non_recursive('0??100?1'))))
此解决方案不会在列表和字符串之间移动,因为不需要也不会操作输入值。你可以看出递归的有点慢,但我更喜欢它的可读性。输出:
['00010001', '00010011', '00110001', '00110011', '01010001', '01010011', '01110001', '01110011']
['00010001', '01010001', '00110001', '01110001', '00010011', '01010011', '00110011', '01110011']
13.073874
3.9742709000000005
请注意,您自己的解决方案 运行 使用相同的设置大约需要 8 秒,因此我建议的 "improved version" 更简单,但速度不快,因此您可能更喜欢后一种解决方案。
我有一个方法可以执行以下操作。问题是我如何对这种方法进行单元测试。我对这个 Python 单元测试模块很陌生。
问题及解决方法如下:
Given a string containing of ‘0’, ‘1’ and ‘?’ wildcard characters, generate all binary strings that can be formed by replacing each wildcard character by ‘0’ or ‘1’.
Example :
Input str = "1??0?101"
Output:
10000101
10001101
10100101
10101101
11000101
11001101
11100101
11101101
解决方案:
def _print(string, index):
if index == len(string):
print(''.join(string))
return
if string[index] == "?":
# replace '?' by '0' and recurse
string[index] = '0'
_print(string, index + 1)
# replace '?' by '1' and recurse
string[index] = '1'
_print(string, index + 1)
# NOTE: Need to backtrack as string
# is passed by reference to the
# function
string[index] = '?'
else:
_print(string, index + 1)
# Driver code
if __name__ == "__main__":
string = "1??0?101"
string = list(string) #don’t forget to convert to string
_print(string, 0)
输出:
10000101
10001101
10100101
10101101
11000101
11001101
11100101
11101101
问题:
1.另外,有没有办法将列表作为输出返回而不是打印出来?
2。哪些断言测试用例适用于这种情况?
3。在这种情况下,最好的端到端测试用例是什么?
4.在时间和 space 复杂性方面,有什么更好的方法可以解决这个问题?
我已经试过了,但似乎不起作用:
import unittest
from wildcard import _print
class TestWildCard(unittest.TestCase):
def test_0_print(self):
print("Start wildCard _print test: \n")
result = 111
self.assertEquals(_print("1?1",0),result,"Results match")
答案:
1: 当然,不是打印一些东西,而是将结果附加到列表 result.append('some value')
并且不要忘记在代码开头初始化列表 result = []
和 return 一旦函数完成 return result
- 并且可能不会调用函数 _print
,而是像 bit_strings
.
ad 1: 由于你的函数是递归的,你现在还需要捕获 return 值并在递归调用函数时将其添加到结果中,所以 result += _print(string, index + 1)
2:您通常应该考虑边缘情况并分别对其进行测试,或者将它们组合在一起以真正测试您的功能的单个方面。没有一种方法可以说明测试应该是什么样子 - 如果有,测试框架会为您生成它。
3:与 2 相同的答案。
您的代码变为:
def bit_strings(s, index):
result = []
if index == len(s):
result.append(''.join(s))
return result
if s[index] == "?":
# replace '?' by '0' and recurse
s[index] = '0'
result += bit_strings(s, index + 1)
# replace '?' by '1' and recurse
s[index] = '1'
result += bit_strings(s, index + 1)
# NOTE: Need to backtrack as string
# is passed by reference to the
# function
s[index] = '?'
else:
result += bit_strings(s, index + 1)
return result
# Driver code
if __name__ == "__main__":
x = "1??0?101"
xl = list(x) #don’t forget to convert to string
print(bit_strings(xl, 0))
有更有效的方法可以做到这一点,但我只是根据问题和答案修改了您的代码。
我已将 string
重命名为 s
,因为 string
有点令人困惑,提醒其他人该类型或隐藏(内置)模块。
至于单元测试:
import unittest
from wildcard import bit_strings
class TestWildCard(unittest.TestCase):
def test_0_print(self):
print("Start wildCard _print test: \n")
# you only had one case here and it's a list now
result = ['101', '111']
# user assertEqual, not Equals
# you were passing in a string, but your code assumed a list, so list() added
self.assertEqual(bit_strings(list("1?1"), 0), result, "Results match")
当使用像 PyCharm 这样的环境时,它有助于调用文件 test<something>.py
(即名称中有 test
),这样它可以帮助您 运行单元测试更容易。
评论中要求的两个替代解决方案(一个仍然是递归的,只是更简洁,另一个不是递归的,但可以说结果列表有点浪费 - 只是两个快速):
from timeit import timeit
def unblank_bits(bits):
if not bits:
yield ''
else:
for ch in '01' if bits[0] == '?' else bits[0]:
for continuation in unblank_bits(bits[1:]):
yield ch + continuation
print(list(unblank_bits('0??100?1')))
def unblank_bits_non_recursive(bits):
result = ['']
for ch in bits:
if ch == '?':
result = [s+'0' for s in result] + [s+'1' for s in result]
else:
result = [s+ch for s in result]
return result
print(list(unblank_bits_non_recursive('0??100?1')))
print(timeit(lambda: list(unblank_bits('0??100?1'))))
print(timeit(lambda: list(unblank_bits_non_recursive('0??100?1'))))
此解决方案不会在列表和字符串之间移动,因为不需要也不会操作输入值。你可以看出递归的有点慢,但我更喜欢它的可读性。输出:
['00010001', '00010011', '00110001', '00110011', '01010001', '01010011', '01110001', '01110011']
['00010001', '01010001', '00110001', '01110001', '00010011', '01010011', '00110011', '01110011']
13.073874
3.9742709000000005
请注意,您自己的解决方案 运行 使用相同的设置大约需要 8 秒,因此我建议的 "improved version" 更简单,但速度不快,因此您可能更喜欢后一种解决方案。