Python Try/Except 块的单元测试
Python Unit Test for Try/Except Block
我有以下单元测试功能:
#!/usr/bin/env python3
# https://docs.python.org/3/library/ipaddress.html
# https://docs.python.org/3.4/library/unittest.html
import ipaddress
import unittest
from unittest.mock import patch
from unittest import TestCase
def validate_IP():
"""Prompt user for IPv4 address, then validate."""
while True:
try:
return ipaddress.IPv4Address(input('Enter a valid IPv4 address: '))
except ValueError:
print('Bad value, try again.')
class validate_IP_Test(unittest.TestCase):
@patch('builtins.input', return_value='192.168.1.1')
def test_validate_IP_01(self, input):
self.assertIsInstance(validate_IP(), ipaddress.IPv4Address)
@patch('builtins.input', return_value='10.0.0.1')
def test_validate_IP_02(self, input):
self.assertIsInstance(validate_IP(), ipaddress.IPv4Address)
@patch('builtins.input', return_value='Derp!')
def test_validate_IP_03(self, input):
self.assertRaises(ValueError, msg=none)
if __name__ == '__main__':
unittest.main()
该函数使用 Python3 中的 ipaddress
模块来验证用户输入,即检查用户输入是否真实 IPv4 address
。我的前两个测试按预期工作。不过,我不清楚如何使用 Python3 的 unittest
模块来测试函数异常部分的无效输入,就像第三个测试一样。
输入无效输入时,测试应识别出异常并通过测试。作为参考,这是我输入无效地址时解释器的相关输出:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File
raise AddressValueError("Expected 4 octets in %r" % ip_str)
ipaddress.AddressValueError: Expected 4 octets in 'derp'`
异常未被识别,因为您捕获了它,您需要不捕获它或重新引发它。要重新加注,只需在 catch 块内不带参数地调用 raise
,在您的示例中:
def validate_IP():
"""Prompt user for IPv4 address, then validate."""
while True:
try:
return ipaddress.IPv4Address(input('Enter a valid IPv4 address: '))
except ValueError:
print('Bad value, try again.')
raise
这会让你跳出那个 while
,让它变得毫无意义。我会在另一种方法或函数中移动它,以便您可以单独测试 IPv4Address 提升行为。
另一个问题是在函数内部调用 input
,这对测试来说非常烦人。我会选择 def validate_ip(ip):
,更容易测试。
此致,
您可以使用 assertRaises
方法作为上下文管理器:
@patch('builtins.input', return_value='Derp!')
def test_validate_IP_03(self, input):
with self.assertRaises(ValueError):
validate_IP()
但是,您的 validate_IP
函数在无限循环中捕获异常本身,因此上述测试实际上不会通过。如果您打算能够在调用之外捕获异常,则可以使其不重试,而是在输出错误消息后重新引发异常:
def validate_IP():
try:
return ipaddress.IPv4Address(input('Enter a valid IPv4 address: '))
except ValueError:
print('Bad IPv4 address.')
raise
无效输入的可见行为是请求新的输入。它是通过捕获异常来完成的,这是测试最好不要关心的实现细节,这样您就可以在不破坏测试的情况下更改该实现细节。您问题中的回溯是 IPv4Address
会做什么,而不是 validate_IP()
会做什么。
此外,当地址无效时,您还期望 AddressValueError
,捕获 ValueError
可能会隐藏其他意外异常并使调试更加困难,因此您最好捕获更具体的异常:
def validate_IP():
"""Prompt user for IPv4 address, then validate."""
while True:
try:
return ipaddress.IPv4Address(input('Enter a valid IPv4 address: '))
except ipaddress.AddressValueError:
print('Bad value, try again.')
您可能希望您的测试讲述的一个故事是:如果输入流包含一个无效的 IP,然后是一个有效的 IP,我们将丢弃前者并 return 后者。
@patch('builtins.input', side_effect=('Derp!', '10.0.0.1'))
def test_invalid_IP(self, input):
self.assertEqual(validate_IP(), ipaddress.IPv4Address('10.0.0.1'))
顺便说一句,我建议你重新考虑这些名称,我认为 validate_IP
并没有真正说明函数在做什么,测试名称几乎没有意义。
我有以下单元测试功能:
#!/usr/bin/env python3
# https://docs.python.org/3/library/ipaddress.html
# https://docs.python.org/3.4/library/unittest.html
import ipaddress
import unittest
from unittest.mock import patch
from unittest import TestCase
def validate_IP():
"""Prompt user for IPv4 address, then validate."""
while True:
try:
return ipaddress.IPv4Address(input('Enter a valid IPv4 address: '))
except ValueError:
print('Bad value, try again.')
class validate_IP_Test(unittest.TestCase):
@patch('builtins.input', return_value='192.168.1.1')
def test_validate_IP_01(self, input):
self.assertIsInstance(validate_IP(), ipaddress.IPv4Address)
@patch('builtins.input', return_value='10.0.0.1')
def test_validate_IP_02(self, input):
self.assertIsInstance(validate_IP(), ipaddress.IPv4Address)
@patch('builtins.input', return_value='Derp!')
def test_validate_IP_03(self, input):
self.assertRaises(ValueError, msg=none)
if __name__ == '__main__':
unittest.main()
该函数使用 Python3 中的 ipaddress
模块来验证用户输入,即检查用户输入是否真实 IPv4 address
。我的前两个测试按预期工作。不过,我不清楚如何使用 Python3 的 unittest
模块来测试函数异常部分的无效输入,就像第三个测试一样。
输入无效输入时,测试应识别出异常并通过测试。作为参考,这是我输入无效地址时解释器的相关输出:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File
raise AddressValueError("Expected 4 octets in %r" % ip_str)
ipaddress.AddressValueError: Expected 4 octets in 'derp'`
异常未被识别,因为您捕获了它,您需要不捕获它或重新引发它。要重新加注,只需在 catch 块内不带参数地调用 raise
,在您的示例中:
def validate_IP():
"""Prompt user for IPv4 address, then validate."""
while True:
try:
return ipaddress.IPv4Address(input('Enter a valid IPv4 address: '))
except ValueError:
print('Bad value, try again.')
raise
这会让你跳出那个 while
,让它变得毫无意义。我会在另一种方法或函数中移动它,以便您可以单独测试 IPv4Address 提升行为。
另一个问题是在函数内部调用 input
,这对测试来说非常烦人。我会选择 def validate_ip(ip):
,更容易测试。
此致,
您可以使用 assertRaises
方法作为上下文管理器:
@patch('builtins.input', return_value='Derp!')
def test_validate_IP_03(self, input):
with self.assertRaises(ValueError):
validate_IP()
但是,您的 validate_IP
函数在无限循环中捕获异常本身,因此上述测试实际上不会通过。如果您打算能够在调用之外捕获异常,则可以使其不重试,而是在输出错误消息后重新引发异常:
def validate_IP():
try:
return ipaddress.IPv4Address(input('Enter a valid IPv4 address: '))
except ValueError:
print('Bad IPv4 address.')
raise
无效输入的可见行为是请求新的输入。它是通过捕获异常来完成的,这是测试最好不要关心的实现细节,这样您就可以在不破坏测试的情况下更改该实现细节。您问题中的回溯是 IPv4Address
会做什么,而不是 validate_IP()
会做什么。
此外,当地址无效时,您还期望 AddressValueError
,捕获 ValueError
可能会隐藏其他意外异常并使调试更加困难,因此您最好捕获更具体的异常:
def validate_IP():
"""Prompt user for IPv4 address, then validate."""
while True:
try:
return ipaddress.IPv4Address(input('Enter a valid IPv4 address: '))
except ipaddress.AddressValueError:
print('Bad value, try again.')
您可能希望您的测试讲述的一个故事是:如果输入流包含一个无效的 IP,然后是一个有效的 IP,我们将丢弃前者并 return 后者。
@patch('builtins.input', side_effect=('Derp!', '10.0.0.1'))
def test_invalid_IP(self, input):
self.assertEqual(validate_IP(), ipaddress.IPv4Address('10.0.0.1'))
顺便说一句,我建议你重新考虑这些名称,我认为 validate_IP
并没有真正说明函数在做什么,测试名称几乎没有意义。