测试给定的输入值是否不会从 while True 循环中中断
Test if given input value does not break from while True loop
我想对名为 prompt_process
的函数进行单元测试。该函数的作用是根据input
:
的return值,提示是调用另一个函数,process
,还是干脆退出程序
process.py
import sys
def process():
pass
def prompt_process():
answer = input("Do you want to process? (y/n)").lower()
while True:
if answer == 'y':
return process()
elif answer == 'n':
sys.exit(0)
else:
answer = input("Invalid answer - please type y or n").lower()
使用模拟测试 if
/elif
子句非常简单:
test_process.py
import unittest
from unittest import mock
import process
class TestProcess(unittest.TestCase):
@mock.patch('process.process')
def test_prompt_process_calls_process_if_input_is_y(self, mock_process):
with mock.patch('process.input', return_value='y'):
process.prompt_process()
mock_process.assert_called_once()
def test_prompt_process_exits_if_input_is_n(self):
with mock.patch('process.input', return_value='n'):
with self.assertRaises(SystemExit):
process.prompt_process()
if __name__ == '__main__':
unittest.main()
但是我不知道如何测试函数的else
子句。理想情况下,它应该测试循环是否为 y
和 n
以外的输入值保持 运行,但由于我无法“模拟”while True
循环,所以我不确定如何进行。
是否有任何解决方案可以对此进行测试,或者这是我一开始就不应该打扰的事情?
在函数内调用 sys.exit(0)
不能被视为最佳做法,可能存在打开的文件、待处理的事务或其他 transactional/transient 待处理的操作。
建议创建一个执行退出的函数,并在要测试的函数内部调用该函数。这样你的功能应该很容易测试。
import sys
def process():
pass
def leave():
sys.exit(0)
def prompt_process():
answer = input("Do you want to process? (y/n)").lower()
while True:
if answer == 'y':
return process()
elif answer == 'n':
return leave()
else:
answer = input("Invalid answer - please type y or n").lower()
无创方式
一种非侵入式的方法是通过使用 side_effect
而不是 return_value
.
引发异常来检查 input
是否被调用两次
这对实现做了一个小假设。
# Define a custom exception to ensure it's not raised elsewhere
class EndError(Exception):
pass
def test_prompt_process_does_not_break_from_while_true_loop_if_input_is_invalid(self):
with mock.patch('process.input', side_effect=('invalid', EndError)):
with self.assertRaises(EndError):
process.prompt_process()
微创方式(无操作功能)
另一种微创的方法是在while
循环中定义并调用一个noop
函数。
def noop():
pass
while True:
noop() # Add this
# ...
@mock.patch('process.noop', side_effect=(None, EndError))
def test_prompt_process_does_not_break_from_while_true_loop_if_input_is_invalid(self, mock_noop):
with mock.patch('process.input', return_value='invalid'):
with self.assertRaises(EndError):
process.prompt_process()
self.assertEqual(mock_noop.call_count, 2)
测试最大迭代次数
如果您的实现限制了尝试次数,那么您不必处理异常。
max_tries = 10
for i in range(max_tries):
noop()
# ...
if i < max_tries - 1:
answer = input("Invalid answer - please type y or n").lower()
else:
_ = input("Invalid answer - press enter to end") # Either
# print("Invalid answer") # or
@mock.patch('process.noop')
def test_prompt_process_accepts_10_tries_if_input_is_invalid(self, mock_noop):
with mock.patch('process.input', return_value='invalid') as mock_input:
process.prompt_process()
self.assertEqual(mock_input.call_count, 11) # This requires knowledge about the implementation
self.assertEqual(mock_noop.call_count, 10) # This only makes an assumption that noop is called
我想对名为 prompt_process
的函数进行单元测试。该函数的作用是根据input
:
process
,还是干脆退出程序
process.py
import sys
def process():
pass
def prompt_process():
answer = input("Do you want to process? (y/n)").lower()
while True:
if answer == 'y':
return process()
elif answer == 'n':
sys.exit(0)
else:
answer = input("Invalid answer - please type y or n").lower()
使用模拟测试 if
/elif
子句非常简单:
test_process.py
import unittest
from unittest import mock
import process
class TestProcess(unittest.TestCase):
@mock.patch('process.process')
def test_prompt_process_calls_process_if_input_is_y(self, mock_process):
with mock.patch('process.input', return_value='y'):
process.prompt_process()
mock_process.assert_called_once()
def test_prompt_process_exits_if_input_is_n(self):
with mock.patch('process.input', return_value='n'):
with self.assertRaises(SystemExit):
process.prompt_process()
if __name__ == '__main__':
unittest.main()
但是我不知道如何测试函数的else
子句。理想情况下,它应该测试循环是否为 y
和 n
以外的输入值保持 运行,但由于我无法“模拟”while True
循环,所以我不确定如何进行。
是否有任何解决方案可以对此进行测试,或者这是我一开始就不应该打扰的事情?
在函数内调用 sys.exit(0)
不能被视为最佳做法,可能存在打开的文件、待处理的事务或其他 transactional/transient 待处理的操作。
建议创建一个执行退出的函数,并在要测试的函数内部调用该函数。这样你的功能应该很容易测试。
import sys
def process():
pass
def leave():
sys.exit(0)
def prompt_process():
answer = input("Do you want to process? (y/n)").lower()
while True:
if answer == 'y':
return process()
elif answer == 'n':
return leave()
else:
answer = input("Invalid answer - please type y or n").lower()
无创方式
一种非侵入式的方法是通过使用 side_effect
而不是 return_value
.
input
是否被调用两次
这对实现做了一个小假设。
# Define a custom exception to ensure it's not raised elsewhere
class EndError(Exception):
pass
def test_prompt_process_does_not_break_from_while_true_loop_if_input_is_invalid(self):
with mock.patch('process.input', side_effect=('invalid', EndError)):
with self.assertRaises(EndError):
process.prompt_process()
微创方式(无操作功能)
另一种微创的方法是在while
循环中定义并调用一个noop
函数。
def noop():
pass
while True:
noop() # Add this
# ...
@mock.patch('process.noop', side_effect=(None, EndError))
def test_prompt_process_does_not_break_from_while_true_loop_if_input_is_invalid(self, mock_noop):
with mock.patch('process.input', return_value='invalid'):
with self.assertRaises(EndError):
process.prompt_process()
self.assertEqual(mock_noop.call_count, 2)
测试最大迭代次数
如果您的实现限制了尝试次数,那么您不必处理异常。
max_tries = 10
for i in range(max_tries):
noop()
# ...
if i < max_tries - 1:
answer = input("Invalid answer - please type y or n").lower()
else:
_ = input("Invalid answer - press enter to end") # Either
# print("Invalid answer") # or
@mock.patch('process.noop')
def test_prompt_process_accepts_10_tries_if_input_is_invalid(self, mock_noop):
with mock.patch('process.input', return_value='invalid') as mock_input:
process.prompt_process()
self.assertEqual(mock_input.call_count, 11) # This requires knowledge about the implementation
self.assertEqual(mock_noop.call_count, 10) # This only makes an assumption that noop is called