Adam Hughes
Adam Hughes

Reputation: 16309

assertRaises in loop: 1 test per iteration

I'm trying to use assertRaises in a loop so I can test multiple errant delimiters ([',', ':', '-']) without having to write a new test for each case. I'm having trouble using assertRaises in a loop. I made a minimum working example of a unittest.TestCase that calls assertRaises in a loop:

import sys
import unittest

def throw_error():
   sys.stderr.write('In throw error!')
   raise TypeError

class Test(unittest.TestCase):

   def test_loop(self):
      for i in range(5):
         self.assertRaises(TypeError, throw_error)

This works, but it only counts as 1 test, when I'd prefer it to be understood as 5 tests. Is there a canonical way to get this type of behavior?

>>> nosetests ../footest.py
In throw error!
In throw error!
In throw error!
In throw error!
In throw error!
.
----------------------------------------------------------------------
Ran 1 test in 0.001s

OK

The main reason I want this behavior is because this seems like too much of a black box, and I will eventually forget this is 5 tests in 1. Maybe writing to stderr as I've done with a custom message is good enough, or do you have a better suggestion?

Falsetru's answer works nicely, but I can't adapt it to my case

Falsetru's answer works as a standalone example, but my test_loop() function needs to be an instance method of TestCase, because there's many attributes and methods that it needs to work with. Therefore, when I adapted his answer to still use TestCase, it no longer works:

import sys
import unittest
import nose.tools as ntools

def throw_error():
   sys.stderr.write('In throw error!')
   raise TypeError

class Test(unittest.TestCase):

   def test_loop(self):
      for i in range(5):
         yield ntools.assert_raises, TypeError, throw_error

This results in output of:

.
----------------------------------------------------------------------
Ran 1 test in 0.001s

OK

Meaning throw_error() is never called.

Upvotes: 1

Views: 683

Answers (2)

Oleksiy
Oleksiy

Reputation: 6567

This is how you do it with unittest.TestCase:

import sys
import unittest
from nose.tools import istest

def throw_error():
   sys.stderr.write('In throw error!')
   raise TypeError

class Test(unittest.TestCase):
    pass

def _create():
    """ Helper method to make functions on the fly """

    @istest
    def func_name(self):
        self.assertRaises(TypeError, throw_error)

    return func_name

def populate(cls, tests):
    """ Helper method that injects tests to the TestCase class """

    for index, problem in enumerate(tests):
        test_method_name = '_'.join(['test', str(index)])
        _method = _create()
        _method.__name__ = test_method_name
        _method.__doc__ = test_method_name
        setattr(cls, _method.__name__, _method)

tests = range(5)

populate(Test, tests)

Here is the output:

$ nosetests  -v
test_0 ... In throw error!ok
test_1 ... In throw error!ok
test_2 ... In throw error!ok
test_3 ... In throw error!ok
test_4 ... In throw error!ok

----------------------------------------------------------------------
Ran 5 tests in 0.031s

OK

Upvotes: 2

falsetru
falsetru

Reputation: 369074

You're using nose which supports tests generator:

from nose.tools import assert_raises                                            

def throw_error():
   raise TypeError

def test_loop():
    for i in range(5):
        yield assert_raises, TypeError, throw_error

Upvotes: 4

Related Questions