Python: как правильно сделать модули видимыми для TestRunner?

после просмотра нескольких презентаций о тестировании django я хочу написать свой собственный TestRunner, чтобы пропустить тесты django и создать лучшие структуры пакетов для своих тестов.

Проблема в том, что мы изменили структуру проекта, и программа запуска тестов не может найти правильный путь для обнаружения тестов. Вот так выглядит мой проект:

project/
  -src/
    - project_name/
      - apps/
      - test/ # Not a good name, i know, will change it
         - some_app/

           - test_models.py
    - manage.py
    - development.db

Теперь, чтобы проверить test_models.py, я хочу сделать это:

$ cd project/src/
$ python manage.py test some_app.test_models

Проблема в том, что средство запуска тестов не может найти этот пакет (some_app) и модуль (test_models.py). Оно меняется, если я жестко запрограммирую имя в программе запуска тестов, но мне не нравится это делать. Вот что я делаю, чтобы заставить его работать.

test_labels = ["%s.%s" % ("project_name.test", l)
                           for l in test_labels
                           if not l.startswith("project_name.test")]

Итак, если вы делаете

$ python manage.py test some_app.test_models

Он будет переписан на:

$ python manage.py test project_name.test.some_app.test_models

И это прекрасно работает.

Я пытался сделать sys.path.append("(...)/project_name/test), но тоже не работает.

Это код моего TestRunner:

class DiscoveryDjangoTestSuiteRunner(DjangoTestSuiteRunner):
    """A test suite runner that uses unittest2 test discovery.
    It's better than the default django test runner, becouse it
    doesn't run Django tests and let you put your tests in different
    packages, modules and classes.

    To test everything in there:
        $ ./manage.py test

    To test a single package/module:

        $ ./manage.py test package
        $ ./manage.py test package.module

    To test a single class:

        $ ./manage.py test package.module.ClassName
    """
    def build_suite(self, test_labels, extra_tests=None, **kwargs):
        suite = None
        discovery_root = settings.TEST_DISCOVERY_ROOT
        if test_labels:
            # This is where I append the path
            suite = defaultTestLoader.loadTestsFromNames(test_labels)
            # if single named module has no tests, do discovery within it
            if not suite.countTestCases() and len(test_labels) == 1:
                suite = None
                discovery_root = import_module(test_labels[0]).__path__[0]

        if suite is None:
            suite = defaultTestLoader.discover(
                discovery_root,
                top_level_dir=settings.BASE_PATH,
                )

        if extra_tests:
            for test in extra_tests:
                suite.addTest(test)

        return reorder_suite(suite, (TestCase,))

person santiagobasulto    schedule 04.05.2012    source источник


Ответы (2)


Ваша иерархия импорта Python начинается с project/src. Таким образом, правильный путь импорта Python для вашего модуля test_modelsproject_name.test.some_app.test_models, поэтому я ожидаю, что это будет тестовая метка.

Но вам не нравится вводить префикс project_name.test каждый раз, когда вы хотите запустить определенный тестовый модуль, так как все ваши тесты будут находиться там. Это нормально: вы решили ввести какое-то неявное неочевидное поведение в обмен на некоторое удобство. Вы определенно не должны ничего добавлять в sys.path, чтобы добиться этого: ключ к здравомыслию импорта Python заключается в том, чтобы ваша иерархия импорта для данной кодовой базы находилась в одном и ровно одном месте; перекрывающиеся записи sys.path вызовут проблемы, такие как двойной импорт одного и того же модуля под разными именами.

На самом деле все, что вам нужно, — это удобство пользовательского интерфейса, и мне кажется, что показанный вами код с тестовыми ярлыками — очевидный способ реализовать это удобство. Вам не нравится жестко закодированный префикс project_name.test, но он должен быть где-то жестко закодирован: исполняющий тест никак не сможет волшебным образом выяснить, что вы хотите добавить к тестовым меткам project_name.test. Если вы хотите, чтобы ваш TestRunner был более общим, вы можете вытащить его в параметр, такой как BASE_TEST_MODULE или что-то подобное, и добавить значение этого параметра к каждой тестовой метке.

person Carl Meyer    schedule 05.05.2012

Прежде чем вы продолжите вкладывать больше времени в свой собственный TestRunner, я определенно рекомендую вам взглянуть на django-nose.

Пользовательский инструмент запуска тестов, предоставленный django-nose, реализует механизм запуска тестов nose, который чрезвычайно гибок и предоставляет множество возможностей для запуска ваших тестов. Он легко переопределяет команду управления по умолчанию test и позволяет настраивать параметры тестирования по умолчанию в модуле settings вашего проекта.

Я действительно рекомендую его по нескольким причинам:

  • Параметры команды test полностью задокументированы (посмотрите на вывод)
  • Nose предоставляет множество подходов для обнаружения тестов
  • Скорее всего, ваши коллеги уже являются опытными пользователями носа.
  • Вам не нужно было писать класс TestRunner самостоятельно
person Filip Dupanović    schedule 04.05.2012
comment
Хорошо, спасибо. Я смотрел на нос. Мне нужно, чтобы мой премьер-министр одобрил это. - person santiagobasulto; 04.05.2012