Understanding with statement to catch ValueError in unittest class
Solution 1
I'm not 100% sure what your question is here ...
TestCase.assertRaises
creates an object that can be used as a context manager (which is why it can be used with the with
statement). When used this way:
with self.assertRaises(SomeExceptionClass):
# code
The context manager's __exit__
method will check the exception information passed in. If it is missing, an AssertionError
will be thrown causing the test to fail. If the exception is the wrong type (e.g. not an instance of SomeExceptionClass
), an AssertionError
will be thrown as well.
Solution 2
It sounds like you understand what the test is doing. You might find it useful to see how you could write the test if assertRaises
did not exist.
def test_zero_hours_total(self):
try:
pay = divide_pay(360.0, {"Alice": 0.0, "Bob": 0.0, "Carol": 0.0})
except ValueError:
# The exception was raised as expected
pass
else:
# If we get here, then the ValueError was not raised
# raise an exception so that the test fails
raise AssertionError("ValueError was not raised")
Note that you don't have to use assertRaises
as a context manager. You can also pass it the exception, a callable, and arguments for that callable:
def test_zero_hours_total(self):
self.assertRaises(ValueError, divide_pay, 360.0, {"Alice": 0.0, "Bob": 0.0, "Carol": 0.0})
AdjunctProfessorFalcon
Updated on June 18, 2022Comments
-
AdjunctProfessorFalcon almost 2 years
New to unittest and Python in general, came across example in a tutorial introduction to unit testing wherein a with statement is used to catch a ValueError.
The script being tested (invoice_calculator.py) is:
def divide_pay(amount, staff_hours): """ Divide an invoice evenly amongst staff depending on how many hours they worked on a project """ total_hours = 0 for person in staff_hours: total_hours += staff_hours[person] if total_hours == 0: raise ValueError("No hours entered") per_hour = amount / total_hours staff_pay = {} for person in staff_hours: pay = staff_hours[person] * per_hour staff_pay[person] = pay return staff_pay
The unit test includes this function in order to catch an edge case wherein
staff_hours = None
:import unittest from invoice_calculator import divide_pay class InvoiceCalculatorTests(unittest.TestCase): def test_equality(self): pay = divide_pay(300.0, {"Alice": 3.0, "Bob": 6.0, "Carol": 0.0}) self.assertEqual(pay, {'Bob': 75.0, 'Alice': 75.0, 'Carol': 150.0}) def test_zero_hours_total(self): with self.assertRaises(ValueError): pay = divide_pay(360.0, {"Alice": 0.0, "Bob": 0.0, "Carol": 0.0}) if __name__ == "__main__": unittest.main()
Regarding the use of the
with
statement intest_zero_hours_total(self)
, what is actually happening here in terms of how this statement works/is being executed?Is the
test_zero_hours_total()
function basically working as follows (layman's description): the expected error should beValueError
(which we're doing by passingValueError
to the functionassertRaises()
) when360.0, {"Alice": 0.0, "Bob": 0.0, "Carol": 0.0}
(which would raise aValueError
individe_pay()
) is passed as arguments to thedivide_pay()
function?