Slaknation
Slaknation

Reputation: 1926

Python: Making global unit test function

I want to make a function that can be used in other unit tests in the script. Here is what I mean:

class TestSomething(unittest.TestCase):
    def __init__(self, title, description):
        self.title = title
        self.description = description
        self.tag = 'ts'

class TestSomething2(unittest.TestCase):
    def __init__(self, title, description):
        self.title = title
        self.description = description
        self.tag = 'ts2'

class TestWhatever(unittest.TestCase):
    def test_whatever(self):
        blah = TestSomething('Happy Gilmore', 'golf movie')
        AttributeCheck(blah, 'Happy Gilmore', 'golf movie', 'ts')

class TestWhatever2(unittest.TestCase):
    def test_whatever(self):
        blah = TestSomething2('Toy Story', 'kids movie')
        AttributeCheck(blah, 'Toy Story', 'kids movie', 'ts2')

class AttributeCheck(unittest.TestCase):
    def __init__(self, element, title, description, tag):
        super(AttributeCheck, self).__init__()
        self.assertEqual(element.title, title)
        self.assertEqual(element.description, description)
        self.assertEqual(element.tag, tag)

    def runTest(self):  # this is what causes problems
        print 'ok'


if __name__ == '__main__':
    unittest.main()

The error I get is: TypeError: __init__() takes at least 3 arguments (2 given) It basically tries to run the AttributeCheck and I think it runs runTest as if it were a test. However, I need the def runTest(self): because if I don't have it then I get the: ValueError: no such test method in <class 'AttributeCheck'>: runTest

Upvotes: 1

Views: 590

Answers (1)

lortimer
lortimer

Reputation: 710

You're using unittest.TestCase in a way that I haven't seen before, and I think is inconsistent with the documentation. My answer uses TestCase how I normally use it, hopefully it answers your question.

As far as having a function that can be used in multiple tests in the script, you can add a function to your test class that does a check for you. If it doesn't have "test" in the name, it won't be run as a test:

class TestWhatever(unittest.TestCase):
    def test_whatever_does_something(self):
        instance = Whatever('Happy Gilmore', 'golf movie', 'ts')
        self._check_attributes(instance, 'Happy Gilmore', 'golf movie', 'ts')

    def _check_attributes(self, element, title, description, tag):
        self.assertEqual(element.title, title)
        self.assertEqual(element.description, description)
        self.assertEqual(element.tag, tag)

This isn't super useful, because your check method is limited to this class. You could import it into another test class if you wanted to, but that's a little messy as far as separation of responsibility goes.

I typically try to have 1 test class per test file, corresponding to exactly one production class. Each 'test' is a method inside a test class. Then, if there's a function I want to run from a lot of test classes, I put it in a separate file that I call "test_helpers.py". You shouldn't make your test helpers inherit from TestCase. You can define a failure exception and raise it from your test helper method.

The following code would be split up over 5 separate files in the same directory. The file names are in comments. Notice that the class Blah lives in 'blah.py' and corresponds to a test class TestBlah in test_blah.py. That's where you test everything to do with Blah.

I stole the code for the FailureException in test_helpers.py directly from the source code for unittest.

#blah.py
class Blah(object):
    def __init__(self, title, description, tag):
        self.title = title
        self.description = description
        self.tag = tag

#test_blah.py
from test_helpers import check_attributes

class TestBlah(unittest.TestCase):
    def test_constructor(self):
        blah = Blah('Happy Gilmore', 'golf movie', 'ts')
        check_attributes(blah, 'Happy Gilmore', 'golf movie', 'ts')

#sub_blah.py
from blah import Blah

class SubBlah(Blah):
    def __init__(self, title, description, tag):
        super(SubBlah, self).__init__()
        self.different_attribute = "I'm Different!"

#test_sub_blah.py
from test_helpers import check_attributes

class TestSubBlah(unittest.TestCase):
    def test_constructor(self):
        sub_blah = SubBlah('Toy Story', 'kids movie', 'sb')
        check_attributes(blah, 'Toy Story', 'kids movie', 'sb')
        self.assertEqual("I'm Different!", sub_blah.different_attribute)

#test_helpers.py
import Exception

def check_attributes(element, title, description, tag):
    if not title == element.title:
            raise FailureException(msg or '%r != %r' % (title, element.title))
        if not description == element.description :
            raise FailureException(msg or '%r != %r' % (description, element.description))
        if not tag == element.tag:
            raise FailureException(msg or '%r != %r' % (tag, element.tag))

class FailureException(Exception):
    #pass here to make it a basic exception
    pass

    #If you  need custom code, you can override __init__
    #def __init__(self, message, errors):
    #
    #   super(FailureException, self).__init__(message)

Upvotes: 2

Related Questions