Testing Exceptions in Python with unittest

  1. Practicing Python: Quick ISBN-10 Validation
  2. Basic ISBN-10 Validation in Python: Part 2
  3. ISBN Validation: Adding Simple Python Unit Tests
  4. Reliable ISBN-13 Validation
  5. ISBN Validator: Making it General Purpose
  6. Simple ISBN-10 to ISBN-13 Conversion
  7. Testing Exceptions in Python with unittest
  8. Using Simple Code Coverage in Python

When we implemented the convert_isbn_10_to_13() method, we decided to use a custom exception (FormatException) to indicate that there was something wrong with the ISBN-10 string that is provided as input.

    def convert_isbn_10_to_13(isbn10_code_string: str) -> str:
        if not ISBNValidator.validate_isbn10(isbn10_code_string):
            raise ISBNValidator.FormatException(f"{isbn10_code_string} is not a valid ISBN-10 code string.")

From a design standpoint, this is a good thing, but it presents a small challenge when we think about how we might test it within the confines of the Python unittest framework. Luckily the developers of the unittest module thought of this and we can assert whether or not a particular function raises a particular exception.

The formatting is slightly different, but within the context of our ISBN practice exercise, the code looks like this:

    def test_convert_isbn_10_to_13(self):
...
        # Test invalid input
        invalid_isbn_10 = "1-55404-294-X"
        with self.assertRaises(ISBNValidator.FormatException):
            ISBNValidator.convert_isbn_10_to_13(invalid_isbn_10)

Some things to notice about this mechanism:

  1. It is really helpful to use custom exceptions in these cases. That way, there is an easy mechanism to check that a function is throwing the proper exception for the right reason, and other (unexpected) exceptions actually cause the test execution to break. This would be more difficult if we merely tried to raise Exception() from the method.
  2. Using the with clause allows for more than one line to be executed while waiting for the exception. If the code finishes the with block without an exception, the test will fail.

Once we integrate this code into our test suite, we are now able to ensure that we are raising an exception when presented with invalid input. Hooray!

Next, we will try to answer the question: “Are my tests exercising all of the code I’ve written?” by learning a bit about Python code coverage tools.

For reference, here is the code in it’s current state.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top