How do I tell django-nose where my tests are?

2019-03-11 03:38发布

问题:

I have my tests for a Django application in a tests directory:

my_project/apps/my_app/
├── __init__.py
├── tests
│   ├── __init__.py
│   ├── field_tests.py
│   └── storage_tests.py
├── urls.py
├── utils.py
└── views.py

The Django test runner requires that I put a suite() function in the __init__.py file of my application's tests directory. That function returns the test cases that will run when I do $ python manage.py test

I installed django-nose. When I try to run the tests with django-nose, 0 tests are run:

$ python manage.py test <app_name>

If I point directly at the test module, the tests are run:

$ python manage.py test my_project.apps.my_app.tests.storage_tests

Why does django-nose's test runner not find my tests? What must I do?

回答1:

If you use django-nose you can use a simple selector:

from nose.selector import Selector
from nose.plugins import Plugin
import os
import django.test

class TestDiscoverySelector(Selector):
    def wantDirectory(self, dirname):
        parts = dirname.split(os.path.sep)
        return 'my_app' in parts

    def wantClass(self, cls):
        return issubclass(cls, django.test.TestCase)

    def wantFile(self, filename):
        parts = filename.split(os.path.sep)
        return 'test' in parts and filename.endswith('.py')

    def wantModule(self, module):
        parts = module.__name__.split('.')
        return 'test' in parts

class TestDiscoveryPlugin(Plugin):
    enabled = True

    def configure(self, options, conf):
        pass

    def prepareTestLoader(self, loader):
        loader.selector = TestDiscoverySelector(loader.config)

This is just an example implementation and you can make it more configurable or adjust it to your needs. To use it in your Django project just provide the following option in settigs.py

NOSE_PLUGINS = ['lorepo.utility.noseplugins.TestDiscoveryPlugin']


回答2:

Wrong:

python manage.py test my_app.tests.storage_tests:TestCase.test_name

is in fact with slashes until the class name and with the extension of the file

Right:

python manage.py test my_app/tests/storage_tests.py:TestCase.test_name


回答3:

nose will pick automatically, if you add test = True in the beginning of module and if your methods starts with test_

see how nose detects tests http://nose.readthedocs.io/en/latest/finding_tests.html



回答4:

I have run into this same situation as well. Using the suite it will allow you to run all tests, or tests for an app, but not a specific test case or a specific test.

It's pretty hacky, but what I've done to get around this is instead of defining a suite in __init__.py is to just import * from all of the other test modules, it sucks but it works.

There are a few things that I do to help make sure that I don't end up clobbering test suites in other modules... have __all__ declared in each test modules so only the test names are imported with the * and keep pylint running I'm notified of class name redefinitions.

Having said that you should be able to get this to work without any ugly import * crap... I do not use nose and django-nose...(which I intend to correct very soon)... since that is what you're doing it looks like you can do this to run all of the tests in your apps directory:

python manage.py test apps

or to run all of the tests for a single test module:

python manage.py test apps.my_app.tests.storage_tests

notice I did not include the project in the previous example... which seemed to work fine for me using nosetests and django-nose.

Also, to run a specific test suite you can do this:

python manage.py test apps.my_app.tests.storage_tests:TestCase

Or to run one specific test:

python manage.py test apps.my_app.tests.storage_tests:TestCase.test_name


回答5:

To make nose realize where your apps are add these to the top of manage.py

import os, site

ROOT = os.path.dirname(os.path.abspath(__file__))
path = lambda *a: os.path.join(ROOT, *a)
site.addsitedir(path('apps'))

Since then you can use

python manage.py test my_app.tests.storage_tests:TestCase.test_name