Code Monkey home page Code Monkey logo

django-discover-runner's Introduction

django-discover-runner

Note

This runner has been added to Django 1.6 as the default test runner. If you use Django 1.6 or above you don't need this app.

An alternative Django TEST_RUNNER which uses the unittest2 test discovery from a base path specified in the settings, or any other module or package specified to the test management command -- including app tests.

If you just run ./manage.py test, it'll discover and run all tests underneath the current working directory. E.g. if you run ./manage.py test full.dotted.path.to.test_module, it'll run the tests in that module (you can also pass multiple modules). If you give it a single dotted path to a package (like a Django app) like ./manage.py test myapp and that package does not itself directly contain any tests, it'll do test discovery in all submodules of that package.

Note

This code uses the default unittest2 test discovery behavior, which only searches for tests in files named test*.py. To override this see the TEST_DISCOVER_PATTERN setting or use the --pattern option.

Why?

Django's own test discovery is very much tied to the directory structure of Django apps, partly due to historic reasons (the unittest library didn't have its own discovery for a long time) and prevents Django app authors from being good Python citizens. django-discover-runner uses the official test discovery feature of the new unittest2 library which is included in Django.

By default there is no way to put project specific tests in a separate folder outside the Python package of the Django project, which is a great way to organize your code, separating the tests and non-test code. django-discover-runner helps you clean up your project tests.

There is also no way to specify fully dotted import paths to test modules, functions, class or methods to the test management command but only Django's odd standard <appname>.<TestClassName>. django-discover-runner allows you to specify any type of label to Django's test management command.

By default Django's test runner will execute the tests of Django's own contrib apps, which doesn't make sense if you just want to run your own app's or project's tests. django-discover-runner fixes this by allowing you to specify which tests to run and organize your test code outside the reach of the Django test runner.

More reasons can be found in Carl Meyer's excellent talk about Testing and Django (slides).

Installation

Install it with your favorite installer, e.g.:

pip install -U django-discover-runner

django-discover-runner requires at least Django 1.4 and also works on 1.5.x. Starting in Django 1.6 the discover runner is a built-in.

Setup

  • TEST_RUNNER (required) needs to point to the DiscoverRunner class to enable it:

    TEST_RUNNER = 'discover_runner.DiscoverRunner'
  • Add 'discover_runner' to your INSTALLED_APPS setting to enable the ability to override the discovery settings below when using the test management command.
  • TEST_DISCOVER_TOP_LEVEL (optional) should be the directory containing your top-level package(s); in other words, the directory that should be on sys.path for your code to import. This is for example the directory containing manage.py in the new Django 1.4 project layout. The management command option is called --top-level.
  • TEST_DISCOVER_PATTERN (optional) is the pattern to use when discovering tests and defaults to the unittest2 standard test*.py. The management command option is called --pattern.

Examples

Django app

To test a reusable Django app it's recommended to add a test_settings.py file to your app package to easily run the app tests with the test management command. Simply set the TEST_RUNNER setting to 'discover_runner.DiscoverRunner', configure the other settings necessary to run your tests and call the test management command with the name of the app package, e.g.:

django-admin.py test --settings=myapp.test_settings myapp

Django project

If you want to test a project and want to store the project's tests outside the project main package (recommended), you can simply follow the app instructions above, applying it to the "project" package, but set a few additional settings to tell the test runner to find the tests:

from os import path
TEST_DISCOVER_TOP_LEVEL = path.dirname(path.dirname(__file__))

This would find all the tests within a top-level "tests" package. Running the tests is as easy as calling:

django-admin.py test --settings=mysite.test_settings tests

Alternatively you can specify the --top-level-directory management command option.

Multiple Django versions

In case you want to test your app on older Django versions as well as Django >= 1.6 you can simply conditionally configure the test runner in your test settings, e.g.:

import django

if django.VERSION[:2] < (1, 6):
  TEST_RUNNER = 'discover_runner.DiscoverRunner'

Changelog

1.0 06/15/2013

  • GOOD NEWS! This runner was added to Django 1.6 as the new default! This version backports that runner for Django 1.4.x and 1.5.x.
  • Removed TEST_DISCOVER_ROOT setting in favor of unittest2's own way to figure out the root.
  • Dropped support for Django 1.3.x.

0.4 04/12/2013

  • Added ability to override the discover settings with a custom test management command.

0.3 01/28/2013

  • Fixed setup.py to work on Python 3. This should make this app compatible to Python 3.

0.2.2 09/04/2012

  • Stopped setting the top level variable in the case of using a module path as the test label as it made the wrong assumption that the parent directory is the top level.

0.2.1 08/20/2012

  • Fixed a rather esoteric bug with testing test case class methods that was caused by a wrong import and the way Django wraps itself around the unittest2 module (if availale) or unittest on Python >= 2.7.

0.2 05/26/2012

  • Added ability to use an optionally installed unittest2 library for Django projects using Django < 1.3 (which added unittest2 to the django.utils.unittest package).

0.1.1 05/23/2012

  • Fixed a bug that prevented the project based feature to work correctly.

0.1 05/20/2012

  • Initial release with support for Django >= 1.3.

Thanks

This test runner is a humble rip-off of Carl Meyer's DiscoveryRunner which he published as a gist a while ago. All praise should be directed at him. Thanks, Carl!

This was also very much related to ticket #17365 which eventually led to the replacement of the default test runner in Django. Thanks again, Carl!

django-discover-runner's People

Contributors

jezdez avatar rafales avatar riquito avatar thedrow avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

django-discover-runner's Issues

"DatabaseError: duplicate column name" - bug with custom model's abstract class

Hi,

I have a problem with django-discover-runner and custom model's abtrsact class.

Example:

lib.py

from django.db import models

class ModelAbstract(models.base.ModelBase):
    def __new__(cls, name, bases, attrs):
        dateable = False
        if 'Meta' in attrs:
            if hasattr(attrs['Meta'], 'dateable'):
                dateable = attrs['Meta'].dateable
                delattr(attrs['Meta'], 'dateable')

     super_new = super(ModelAbstract, cls).__new__(cls, name, bases, attrs)

     if hasattr(super_new, '_meta') and not super_new._meta.abstract:
         if hasattr(super_new._meta, 'dateable'):
             dateable = super_new._meta.dateable
         else:
             super_new._meta.dateable = dateable

         if dateable:
             super_new.add_to_class('created_date',
                 models.DateTimeField('created at', auto_now_add=True))
       return super_new


class BaseModel(models.Model):
    __metaclass__ = ModelAbstract

    class Meta:
        abstract = True

models.py

class MyModel(BaseModel):
    name = models.CharField(max_length=100)

    class Meta:
        dateable = True

tests.py

from django.test import TestCase
from .models import MyModel

class SetUpProjectTestCase(TestCase):
    def setUp(self):
        self.obj = MyModel()
        self.obj.name = '...'
        self.obj.save()

    def tearDown(self):
        self.obj.delete()

settings.py

INSTALLED_APPS = (
    'discover_runner',
    'myapp',
)
TEST_RUNNER = 'discover_runner.DiscoverRunner'

django-discover-runner (whole project):

python manage.py test --settings=mysettings

Creating test database for alias 'default'...
DatabaseError: duplicate column name: created_date

:(

django-discover-runner (per app):

python manage.py test myapp --settings=mysettings

OK

Django default test (whole project):

python manage.py test --settings=mysettings

OK

Django default test (per app):

python manage.py test myapp --settings=mysettings

OK

What is wrong with django-discover-runner and test for whole project?

Trying to test multiple apps.... Ran 0 tests in 0.000s

I have 10+ apps that I want to test and I'm trying to get it to work with django-discover-runner.

If I run one, it looks fine:

± % ./manage.py test content --configuration=Test
Creating test database for alias 'default'...
/home/django/.virtualenvs/astrobiology-alpha/local/lib/python2.7/site-packages/django/utils/hashcompat.py:9: DeprecationWarning: django.utils.hashcompat is deprecated; use hashlib instead
  DeprecationWarning)

.../home/django/.virtualenvs/astrobiology-alpha/local/lib/python2.7/site-packages/django/utils/text.py:221: DeprecationWarning: This function has been deprecated. Use the Truncator class in django.utils.text instead.
  'in django.utils.text instead.', category=DeprecationWarning)

............
----------------------------------------------------------------------
Ran 15 tests in 2.784s

OK
Destroying test database for alias 'default'...

Tests are in file 'test_models.py' in content/tests/

If I run another one, it also looks fine:

± % ./manage.py test groups --configuration=Test
/home/django/.virtualenvs/astrobiology-alpha/local/lib/python2.7/site-packages/django/utils/hashcompat.py:9: DeprecationWarning: django.utils.hashcompat is deprecated; use hashlib instead
  DeprecationWarning)

Creating test database for alias 'default'...
..........E.....
======================================================================
ERROR: test_team_members (tests.test_models.TeamTest)
----------------------------------------------------------------------
Traceback (most recent call last):
.
.
.
IntegrityError: duplicate key value violates unique constraint "reports_naiprojectreport_pkey"


----------------------------------------------------------------------
Ran 16 tests in 0.442s

FAILED (errors=1)
Destroying test database for alias 'default'...

Ok, failed test, but it still found and ran them.

Test file test_models.py in groups/tests/

Let's try running both:

± % ./manage.py test content groups --configuration=Test --traceback
Creating test database for alias 'default'...
/home/django/.virtualenvs/astrobiology-alpha/local/lib/python2.7/site-packages/django/utils/hashcompat.py:9: DeprecationWarning: django.utils.hashcompat is deprecated; use hashlib instead
  DeprecationWarning)


----------------------------------------------------------------------
Ran 0 tests in 0.000s

OK
Destroying test database for alias 'default'...

Am I doing something wrong? The above should be valid, right?

Here is my Test configuration:

class Test(Base):

    DATABASES = {
        'default': {
            'ENGINE': 'django.contrib.gis.db.backends.postgis',
            'NAME': 'astrobiology_' + BUILD,
            'USER': 'astrobiology',
            'PASSWORD': 'xxxxxxxxxx',
        },
    }

    @property
    def INSTALLED_APPS(self):
        return (
            #'django_coverage',
            'django_nose',
            'discover_runner',
            'django_jenkins',
        ) + super(Test, self).INSTALLED_APPS

    JENKINS_TASKS = (
        'django_jenkins.tasks.with_coverage',
        'django_jenkins.tasks.django_tests',   # select one django or
        'django_jenkins.tasks.dir_tests',      # directory tests discovery
        'django_jenkins.tasks.run_pep8',
        #'django_jenkins.tasks.run_flake8',
        'django_jenkins.tasks.run_pyflakes',
        'django_jenkins.tasks.run_graphmodels',
        'django_jenkins.tasks.run_pylint',
        'django_jenkins.tasks.run_csslint',
        'django_jenkins.tasks.run_sloccount',
        'django_jenkins.tasks.run_jshint',
        'django_jenkins.tasks.lettuce_tests',
    )

    TEST_RUNNER = 'discover_runner.DiscoverRunner'
    #TEST_DISCOVER_TOP_LEVEL = TEST_DISCOVER_ROOT = PROJECT_ROOT
    #JENKINS_TEST_RUNNER = 'django_jenkins.nose_runner.CINoseTestSuiteRunner'
    DATABASE_ROUTERS = []

Is there anything I'm doing wrong?

Thanks!

Django models not loaded with this test runner.

Hi,

It looks like this test runner doesn't validate models before starting the tests.
I have one test that passes with Django's default test runner while it fails with django-discover-runner.

The exception is:
ValueError: Cannot create form field for 'vat' yet, because its related model 'vat.VAT' has not been loaded yet

I will try to make a test project since this one isn't open sourced.

Run individual test case

Using ./manage.py test full.dotted.path.to.test_module syntax it is possible to all tests in a module. However, it is not possible to run a specific test inside the test module. This issue proposes that ./manage.py test full.dotted.path.to.test_module.test_specific syntax be supported, which runs the test test_specific inside of test_module only.

This is useful for picking a particular Selenium test to run for development.

Stop on first failure?

Is it possible to configure django-discover-runner so that it stops running tests after it encounters a test failure or error?

Test only models?

Hello,

Where can I setup a model for tests only? (For testing abstract models)

In django 1.5 (without installing this app, obviously), I can define the model in tests.py.
With django-nose models defined in test files are installed automatically as well.

Does the discover-runner support this in any way? Without explicitly calling syncdb from the tests?

Thank you!

Please include the License file in distributed packages

HI,
I just got django-discover-runner from pypi. Sadly, there is no license file included, as distributed via github clone.

For several reasons it'd be great, if you also could include the license in the package (tarball) to be downloaded e.g. from pypi. Thanks.

Add signal for finished tests

Add signal to DiscoverRunner which allows other apps to get the number of failed tests.

Original discussion here: https://twitter.com/jezdez/status/322755071878234112

What I want to do is to write an app which would generate system notifications when tests fail/succeed. With my hacked-up version this is what I get:

notification

For this I need to get the number of failed tests from discover runner.

TypeError: Item in ``from list'' not a string

I'm using Django 1.5 on Python 2.7.

$ cat config/settings.py | grep TEST_
TEST_RUNNER = 'discover_runner.DiscoverRunner'
TEST_DISCOVER_TOP_LEVEL = PROJECT_ROOT
TEST_DISCOVER_ROOT = PROJECT_ROOT
TEST_DISCOVER_PATTERN = 'test*.py'

$ ./manage.py test
Traceback (most recent call last):
  File "./manage.py", line 10, in <module>
    execute_from_command_line(sys.argv)
  File "/home/danilo/.virtualenvs/pcf/lib/python2.7/site-packages/django/core/management/__init__.py", line 453, in execute_from_command_line
    utility.execute()
  File "/home/danilo/.virtualenvs/pcf/lib/python2.7/site-packages/django/core/management/__init__.py", line 392, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/home/danilo/.virtualenvs/pcf/lib/python2.7/site-packages/django/core/management/commands/test.py", line 49, in run_from_argv
    super(Command, self).run_from_argv(argv)
  File "/home/danilo/.virtualenvs/pcf/lib/python2.7/site-packages/django/core/management/base.py", line 218, in run_from_argv
    parser = self.create_parser(argv[0], argv[1])
  File "/home/danilo/.virtualenvs/pcf/lib/python2.7/site-packages/django/core/management/commands/test.py", line 52, in create_parser
    test_runner_class = get_runner(settings, self.test_runner)
  File "/home/danilo/.virtualenvs/pcf/lib/python2.7/site-packages/django/test/utils.py", line 129, in get_runner
    test_module = __import__(test_module_name, {}, {}, test_path[-1])
TypeError: Item in ``from list'' not a string

Using the normal Django test runner, everything works. Is this error related to django-discover-runner?

Wheel file on PyPI is not correct

Wheel uploaded to PyPI contains management command, that references non-existing TEST_DISCOVER_ROOT, so running ./manage.py test is not possible.

The source package does not have management command at all and everything works OK.

This is what I get when package is installed via wheel:

$ ./manage.py test
Traceback (most recent call last):
  File "./manage.py", line 10, in <module>
    execute_from_command_line(sys.argv)
  File "/home/paczkowski/.virtualenvs/itemizer/local/lib/python2.7/site-packages/django/core/management/__init__.py", line 453, in execute_from_command_line
    utility.execute()
  File "/home/paczkowski/.virtualenvs/itemizer/local/lib/python2.7/site-packages/django/core/management/__init__.py", line 392, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/home/paczkowski/.virtualenvs/itemizer/local/lib/python2.7/site-packages/django/core/management/__init__.py", line 272, in fetch_command
    klass = load_command_class(app_name, subcommand)
  File "/home/paczkowski/.virtualenvs/itemizer/local/lib/python2.7/site-packages/django/core/management/__init__.py", line 77, in load_command_class
    module = import_module('%s.management.commands.%s' % (app_name, name))
  File "/home/paczkowski/.virtualenvs/itemizer/local/lib/python2.7/site-packages/django/utils/importlib.py", line 35, in import_module
    __import__(name)
  File "/home/paczkowski/.virtualenvs/itemizer/local/lib/python2.7/site-packages/discover_runner/management/commands/test.py", line 7, in <module>
    class Command(TestCommand):
  File "/home/paczkowski/.virtualenvs/itemizer/local/lib/python2.7/site-packages/discover_runner/management/commands/test.py", line 11, in Command
    default=settings.TEST_DISCOVER_ROOT,
AttributeError: 'module' object has no attribute 'TEST_DISCOVER_ROOT'

Specifying individual test methods

Is it possible to specify individual test methods on the command line? If I try it like this:

python manage.py test myapp.tests.MyTestClass.test_my_method

Then I get an error like this:

TypeError: unbound method test_my_method() must be called with MyTestClass instance as first argument (got nothing instead)

Failed import in test gives odd test error

(First-up, many thanks for this excellent tool).

Here's my basic setup (all tests in one directory with sub directories for each apps tests:

- manage.py
- project/
-- appA/
--- stuff.py
-- tests/
--- appA/
---- test_for_stuff.py

I was writing a new test file, and I had an import error (I'd written from appA import X, instead of from appA.models import X)

When I ran the test by running:

 ./manage.py test project.appA.tests.test_for_stuff

I got the following error:

  File "/home/bird/Dev/aptivate/econsensus/django/econsensus/.ve/local/lib/python2.7/site-packages/django/test/simple.py", line 380, in run_tests
    suite = self.build_suite(test_labels, extra_tests)
  File "/home/bird/Dev/aptivate/econsensus/django/econsensus/.ve/local/lib/python2.7/site-packages/discover_runner/runner.py", line 40, in build_suite
    suite = self.test_loader.loadTestsFromNames(test_labels)
  File "/usr/lib/python2.7/unittest/loader.py", line 128, in loadTestsFromNames
    suites = [self.loadTestsFromName(name, module) for name in names]
  File "/usr/lib/python2.7/unittest/loader.py", line 100, in loadTestsFromName
    parent, obj = obj, getattr(obj, part)
AttributeError: 'module' object has no attribute 'test_for_stuff'

I was a little confused by this because I thoguht it was trying to tell me my test_for_stuff file wasn't there.

I would have expected an ImportError.

Cannot re-run failed tests within PyCharm

From PyCharm (JetBrains IDEA IDE), I'm used to re-runnin only failed tests as it speeds up the testing enormously. However, when using django-discover-runner, this functionality is broken. PyCharm provides the tests to be run in the same way as Django expects them. This app.tests.MyTestCase is provided as app.MyTestCase.

[python-path] /Applications/PyCharm.app/helpers/pycharm/django_test_manage.py test app.MyTest.test_method [project-path]

So could django-discover-runner also discover tests when provided as using the 'Django convention'?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.