Sunday, September 11, 2011

How to discover unit tests from your project

Within code snippet below implemented very primitive way to collect your unit tests within your project.  This is not for real project - I know at least two of fancy discovering tools - it's nose and discover , which do same thing in more convenient and accurate way.

I was curious and I did it.


# -*- coding: utf-8 -*-
import os
import sys
import unittest


FILE_ABSPATH = lambda p: os.path.dirname(os.path.abspath(p))
COMPARE_DIRS = lambda a, b: FILE_ABSPATH(a) == FILE_ABSPATH(b)


def collect_tests():
    """
    Function to collect tests from whole project
    """

    basedir = os.path.abspath(os.path.dirname(__file__))
    main_module = os.path.basename(basedir)

    test_file = "tests.py"
    test_dir = "tests"
    tests = []
    new_locals = {}

    for root, dirs, files in os.walk(basedir):
        if test_file in files and \
            os.path.join(root, test_file) != os.path.abspath(__file__):
            tests.append(os.path.join(root, test_file))

        if os.path.dirname(root) == test_dir:
            tests.append(root)

    for test in tests:
        test_path = test.replace(basedir, '').replace('.py', '')
        module_name = "%s%s" % (main_module, test_path.replace('/', '.'))
        __import__(module_name)

        module = sys.modules[module_name]

        for cls in dir(module):
            attr = getattr(module, cls)
            try:
                attr.__module__
            except AttributeError:
                continue

            # we don't want to import anything from current file
            if COMPARE_DIRS(sys.modules[attr.__module__].__file__, __file__):
                continue

            try:
                if issubclass(attr, unittest.TestCase):
                    new_locals[cls] = attr
            except TypeError:
                pass

    return new_locals

# execute tests
if __name__ == '__main__':
    locals().update(collect_tests())
    unittest.main()