I have the following function with unit tests:
#!/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()
The function uses the ipaddress
module in Python3 to validate user input, i.e. checks that user input is an actual IPv4 address
. My first two tests work as expected. I'm not clear, though, on how to test for invalid input using Python3's unittest
module for the exception portion of the function, as in the third test.
When invalid input is entered, the test should recognize that an exception was thrown and pass the test. For reference, here's the relevant output from an interpreter when I enter an invalid address:
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'`
You can use the assertRaises
method as a context manager:
@patch('builtins.input', return_value='Derp!')
def test_validate_IP_03(self, input):
with self.assertRaises(ValueError):
validate_IP()
However, your validate_IP
function catches the exception itself within an infinite loop so the above test won't actually pass. You can make it not retry, and re-raise the exception after outputting the error message instead, if you intend to be able to catch the exception outside the call:
def validate_IP():
try:
return ipaddress.IPv4Address(input('Enter a valid IPv4 address: '))
except ValueError:
print('Bad IPv4 address.')
raise
The exception is not recognized because you catch it, you need to not catch it or re-raise it. To re-raise just call raise
without argument, inside a catch block, in your example:
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
This will make you step out of that while
, making it non-sense. I would move that while for another method, or function so that you can test IPv4Address raising behavior alone.
Another problem is calling input
inside a function, is very annoying for testing. I would go for def validate_ip(ip):
, much easier to test.
Regards,
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With