views:

244

answers:

4

Hello, I'm just a beginner in Python and programming in general, and have some questions about the unittest module.

I have a class, and in the __init__ method I am doing some assertions to check for bad arguments. I would like to create a unittest which checks for such AssertionError when creating new instances.

In the unittest module, one can test (with assertRaises) for specific exception when a callable is called, but obviously that will apply to methods of the class. What is the proper way to run such test for the constructor?

p.s. I know that I can just try to create an instance of the class with bad arguments, and unittest will report a test failure, but that stops immeditately after the first such exception, and even if I can wrap multile tests in multiple test functions, it just doesn't seem elegant at all...

+1  A: 

Well, as a start, checking for bad arguments is not a good idea in python. Python is dynamically strong typed for a reason.

You should just assume that the arguments are good arguments. You never know your class' users' intent so by checking for good arguments is a way to limit usage of your class in more generic instances.

Instead define a good API and document it very well, using docstrings and text, and leave errors of bad arguments flow automatically to the user.

Example:

def sum_two_values(value_a, value_b):
    return value_a + value_b

okay, this example is stupid, but if I check and assert the value as integer, the function won't work with floats, strings, list, for no reason other than my check, so why check in first place? It will automatically fail with types that it wouldn't work, so you don't have to worry.

nosklo
Well, in my particular case I would like to assert that something has gone wrong early, since later I load some C libraries with ctypes and have to pass to them arguments from the constructor...
@asen_asenov: In that case, just wrap your ctypes call with the appropriate transformation e.g. ctypes.c_int(param). That will make sure the type is compatible and will raise an error for you on incompatible types automatically;
nosklo
+2  A: 

Don't mess with assertRaises. It's too complicated.

Do this

class Test_Init( unittest.TestCase ):
    def test_something( self ):
        try:
            x= Something( "This Should Fail" )
            self.fail( "Didn't raise AssertionError" )
        except AssertionError, e:
            self.assertEquals( "Expected Message", e.message )
            self.assertEquals( args, e.args )

Any other exception will be an ordinary test error.

Also, don't mess with too much up-front error checking in an __init__ method. If someone provides an object of the wrong type, your code will fail in the normal course of events and raise a normal exception through normal means. You don't need to "pre-screen" objects all that much.

S.Lott
+2  A: 

In the unittest module, one can test (with assertRaises) for specific exception when a callable is called, but obviously that will apply to methods of the class. What is the proper way to run such test for the constructor?

The constructor is itself a callable:

self.assertRaises(AssertionError, MyClass, arg1, arg2)

That being said, I want to echo nosklo's and S.Lott's concerns about doing type checking of arguments. Furthermore, you shouldn't be using assertions to check function arguments: assertions are most useful as sanity checks that won't get triggered unless something is internally wrong with your code. Also, assert statements are compiled out when Python is run in "optimized" -O mode. If a function needs to do some sort of checking of its arguments, it should raise a proper exception.

Miles
-1: Assertions are a great way to check arguments. Very short code: assert 0 <= n, "n is negative".
S.Lott
I guess that's a matter of opinion, then.
Miles
A: 

S.Lott's answer is incorrect: self.fail() itself raises an exception, which will then be cause by the except in the following line:

class NetworkConfigTest1(unittest.TestCase):
    def runTest(self):
        try:
            NetworkConfig("192.168.256.0/24")
            self.fail("Exception expected but not thrown")
        except Exception, error:
            printf("Exception caught: %s" % str(error)
            pass

The output was "Exception expected but not thrown" but the unit test was not marked as a failure, even though the code being tested had not been written!

A more valid way to check if a method raises an exception would be to use:

self.failUnlessRaises([error], [callable], [arguments to callable])

In my case the class I'm testing is called NetworkConfig, and the constructor must throw an exception if the network descriptor is invalid. And what worked is:

class NetworkConfigTest1(unittest.TestCase):
    def runTest(self):
        self.failUnlessRaises(Exception, NetworkConfig, "192.168.256.0/24")

This works as desired and performs the correct test.