Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I unit test an __init__() method of a python class with assertRaises()?

I have a class:

class MyClass: def __init__(self, foo):     if foo != 1:         raise Error("foo is not equal to 1!") 

and a unit test that is supposed to make sure the incorrect arg passed to the constructor properly raises an error:

def testInsufficientArgs(self):     foo = 0     self.assertRaises((Error), myClass = MyClass(Error, foo)) 

But I get...

NameError: global name 'Error' is not defined 

Why? Where should I be defining this Error object? I thought it was built-in as a default exception type, no?

like image 604
Sam McAfee Avatar asked Sep 17 '08 22:09

Sam McAfee


People also ask

What does Unittest main () do?

Internally, unittest. main() is using a few tricks to figure out the name of the module (source file) that contains the call to main() . It then imports this modules, examines it, gets a list of all classes and functions which could be tests (according the configuration) and then creates a test case for each of them.

How do you test that a Python function throws an exception?

There are two ways you can use assertRaises: using keyword arguments. Just pass the exception, the callable function and the parameters of the callable function as keyword arguments that will elicit the exception. Make a function call that should raise the exception with a context.


1 Answers

'Error' in this example could be any exception object. I think perhaps you have read a code example that used it as a metasyntatic placeholder to mean, "The Appropriate Exception Class".

The baseclass of all exceptions is called 'Exception', and most of its subclasses are descriptive names of the type of error involved, such as 'OSError', 'ValueError', 'NameError', 'TypeError'.

In this case, the appropriate error is 'ValueError' (the value of foo was wrong, therefore a ValueError). I would recommend replacing 'Error' with 'ValueError' in your script.

Here is a complete version of the code you are trying to write, I'm duplicating everything because you have a weird keyword argument in your original example that you seem to be conflating with an assignment, and I'm using the 'failUnless' function name because that's the non-aliased name of the function:

class MyClass:     def __init__(self, foo):         if foo != 1:             raise ValueError("foo is not equal to 1!")  import unittest class TestFoo(unittest.TestCase):     def testInsufficientArgs(self):         foo = 0         self.failUnlessRaises(ValueError, MyClass, foo)  if __name__ == '__main__':     unittest.main() 

The output is:

. ---------------------------------------------------------------------- Ran 1 test in 0.007s  OK 

There is a flaw in the unit testing library 'unittest' that other unit testing frameworks fix. You'll note that it is impossible to gain access to the exception object from the calling context. If you want to fix this, you'll have to redefine that method in a subclass of UnitTest:

This is an example of it in use:

class TestFoo(unittest.TestCase):     def failUnlessRaises(self, excClass, callableObj, *args, **kwargs):         try:             callableObj(*args, **kwargs)         except excClass, excObj:             return excObj # Actually return the exception object         else:             if hasattr(excClass,'__name__'): excName = excClass.__name__             else: excName = str(excClass)             raise self.failureException, "%s not raised" % excName      def testInsufficientArgs(self):         foo = 0         excObj = self.failUnlessRaises(ValueError, MyClass, foo)         self.failUnlessEqual(excObj[0], 'foo is not equal to 1!') 

I have copied the failUnlessRaises function from unittest.py from python2.5 and modified it slightly.

like image 151
Jerub Avatar answered Oct 01 '22 01:10

Jerub