Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does Python's unittest module detect test cases?

Tags:

I was wondering when we run unittest.main(), how does Python know what subclasses unittest.Testcase has?

For example, if I add a class FromRomanBadInput(unittest.TestCase), how does unittest know to run this?

like image 340
stupidguy Avatar asked Apr 11 '12 03:04

stupidguy


2 Answers

So I looked around in my Python27/Lib directory...

unittest.main is actually an alias for a class, unittest.TestProgram. So what happens is you construct an instance of this, and its __init__ runs, which does a bunch of sanity checks and configuration, including a dynamic import of the module that you called it from (it uses the __import__ function, with __main__ as the name of the module to import, by default). So now it has a self.module attribute that contains a module object that represents your source.

Eventually, it gets to this code:

self.test = self.testLoader.loadTestsFromModule(self.module) 

where self.testLoader is an instance of unittest.TestLoader. That method contains, among other stuff:

    for name in dir(module):         obj = getattr(module, name)         if isinstance(obj, type) and issubclass(obj, case.TestCase):             tests.append(self.loadTestsFromTestCase(obj)) 

So it uses the dir of your module object to get the names of all the global variables you defined (including classes), filters that to just the classes that derive from unittest.TestCase (locally, case.TestCase is an alias for that), and then looks for test methods inside those classes to add to the tests list. That search behaves similarly:

    def isTestMethod(attrname, testCaseClass=testCaseClass,                      prefix=self.testMethodPrefix):         return attrname.startswith(prefix) and \             hasattr(getattr(testCaseClass, attrname), '__call__')     testFnNames = filter(isTestMethod, dir(testCaseClass)) 

so it uses the dir of the class to get a list of names to try, looks for attributes with those names, and selects those that start with the self.testMethodPrefix ('test' by default) and that are callable (have, in turn, a __call__ attribute). (I'm actually surprised they don't use the built-in callable function here. I guess this is to avoid picking up nested classes.)

like image 166
Karl Knechtel Avatar answered Oct 05 '22 09:10

Karl Knechtel


the 'main' function searches for all classes which inherits the unittest.TestCase in imported modules. and current path, then tries to run each method that starts with 'test'

from python's document:

import random import unittest  class TestSequenceFunctions(unittest.TestCase):      def setUp(self):         self.seq = range(10)      def test_shuffle(self):         # make sure the shuffled sequence does not lose any elements         random.shuffle(self.seq)         self.seq.sort()         self.assertEqual(self.seq, range(10))          # should raise an exception for an immutable sequence         self.assertRaises(TypeError, random.shuffle, (1,2,3))      def test_choice(self):         element = random.choice(self.seq)         self.assertTrue(element in self.seq)      def test_sample(self):         with self.assertRaises(ValueError):             random.sample(self.seq, 20)         for element in random.sample(self.seq, 5):             self.assertTrue(element in self.seq)  if __name__ == '__main__':     unittest.main() 

A testcase is created by subclassing unittest.TestCase. The three individual tests are defined with methods whose names start with the letters test. This naming convention informs the test runner about which methods represent tests.

like image 38
pylover Avatar answered Oct 05 '22 08:10

pylover