user3717115
user3717115

Reputation:

Exception raising does not reflect in test case even though raised as seen in logs

I'm practicing TDD in Python and came across a problem in testing whether an exception is raised.

Here is my test_phonebook.py with test_add_empty_name_raises_exception which fails.

import unittest
import phonebook


class Test(unittest.TestCase):

    def test_add_empty_name_raises_exception(self):
        self.assertRaises(ValueError, phonebook.add, "", "1111111111")

if __name__ == "__main__":
    # import sys;sys.argv = ['', 'Test.testName']
    unittest.main()

Below is my phonebook.py with the method add which adds the data into the dictionary.

import re

_phonebook = {}
file_name = "phonebook.txt"


def is_valid_name(name):
    return re.search(r"([A-Z][a-z]*)([\\s\\\'-][A-Z][a-z]*)*", name) is not None


def is_valid_number(number):
    return re.search(r"\+?[\d ]+$", number) is not None


def add(name, number):
    try:
        if is_valid_name(name) and is_valid_number(number):
            _phonebook[name] = number
        else:
            raise ValueError("Invalid arguments.", name, number)
    except ValueError as err:
        print err.args


if __name__ == '__main__':
    pass

My problem is that the test fails even though it is seen in the console log that there was a ValueError raised within the add method.

Finding files... done.
Importing test modules ... done.

('Invalid arguments.', '', '1111111111')
======================================================================
FAIL: test_add_empty_name_raises_exception (path.to.phonebook.test_phonebook.Test)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "path\to\phonebook\test_phonebook.py", line 13, in test_add_empty_name_raises_exception
    self.assertRaises(ValueError, phonebook.add, "", "1111111111")
AssertionError: ValueError not raised

----------------------------------------------------------------------
Ran 1 test in 0.002s

How do I solve this? I there something I forgot?

I also tried using the new format for handling exceptions in tests in Python 2.7 but it still hasn't caught the ValueError raising.

def test_add_empty_name_raises_exception(self):
    with self.assertRaises(ValueError):
        self.phonebook.add("", "1111111111)

I also changed the form of the test case into using lambdas but still no changes.

def test_add_empty_name_raises_exception(self):
    self.assertRaises(ValueError, lambda: phonebook.add("", "1111111111"))

I also cleaned my directory and restarted Eclipse Luna and problem still persists.

Possible solution

I was reading the 8.Errors and Exceptions documentation and got to the "Raising Exceptions" part which states that:

If you need to determine whether an exception was raised but don’t intend to handle it, 
a simpler form of the raise statement allows you to re-raise the exception:

I added this to the existing add method as such:

def add(name, number):
    try:
        if is_valid_name(name) and is_valid_number(number):
            _phonebook[name] = number
            print "Added %s:\t%s" % (name, number)
        else:
            raise ValueError("Invalid arguments.", name, number)
    except ValueError as err:
        print err.args
        raise

Which caused the test case to pass.

Is this the correct way? To call raise again in the except block?

Upvotes: 0

Views: 2240

Answers (1)

Kevin J. Chase
Kevin J. Chase

Reputation: 3956

When you catch an exception (in your except ValueError as err: block), you prevent it from continuing back up the call stack to eventually terminate the program. Essentially, you're saying "I know how to handle this, so no need to panic anyone else."

Re-raising an exception is the proper thing to do if you caught the exception but didn't do so to actually fix anything, for instance, to log that it occurred. Typically, though, one catches an exception in order to correct it.

In your case, you're catching the exception almost immediately after you yourself raised it. Why not put your logging statement in the same else block as the raise? No need for a try: ... except: indent at all.

def add(name, number):
    if is_valid_name(name) and is_valid_number(number):
        _phonebook[name] = number
        print "Added %s:\t%s" % (name, number)
    else:
        print "Invalid arguments.", name, number
        raise ValueError("Invalid arguments.", name, number)
    return

Upvotes: 2

Related Questions