How does Python's unittest module detect test cases?
python, unit-testing
Solution
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.)
Problem
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?