Wednesday, February 23, 2011

Setting up Nose for Django and Hudson

pip install coverage
pip install nose
pip install django_nose
pip install nose-exclude
pip install git+git://

2. The Django nose app works by injecting extra options before the 'test' management command is called. This way, you can invoke ' test' and still have all the options that are available to you if you were to invoke Nose with a normal Python app:

TestRunner = get_runner(settings)

if hasattr(TestRunner, 'options'):
    extra_options = TestRunner.options
    extra_options = []

class Command(Command):
    option_list = Command.option_list + tuple(extra_options)

3. One issue encountered is that if you have a 'setup' as a Django app, nose will report issues since it attempts to search through your main directory looking for your own test suite runner that imports from django_nose:
from django_nose import NoseTestSuiteRunner
from nose.suite import ContextSuite
ContextSuite.moduleSetup = ('setup_module', 'setupModule', 'setUpModule', 'setupHolder',     'setUp')
MyTestSuiteRunner = NoseTestSuiteRunner

Inside the file, you would then set your test runner to:
TEST_RUNNER = 'myapp.test.nose_utils.MyTestSuiteRunner'
# The --with-coverage allows coverage reports to be generated, but we need to
# specify that HTML outputs should be generated and use the Hudson HTML
# Publisher to post them within the jobs.  
# Without the --exe, Nose will  ignore executable files.
# We need the --cover-package to force it to look only in the current directory.
# The --nocapture allows us to see what's going on at stdout and use pdb for breaking checking.# Set --testmatch to look for files that start only with tests.
NOSE_ARGS = ( '--with-coverage', '--cover-html', '--cover-html-dir=xmlrunner/html', '--cover-package=myapp', '--nocapture', '--testmatch=^test')
HUDSON_NOSE_ARGS = ('--with-xunit', '--xunit-file=xmlrunner/nosetests.xml', '--with-xcoverage', '--xcoverage-file=coverage.xml')

You should change --cover-package accordingly.

4. Your Hudson test command would be something similar to the following
./ test --settings=settings.hudson --testmatch="^test" --with-xunit --xunit-file=xmlrunner/nosetests.xml --with-xcoverage --xcoverage-file=coverage.xml --noinput

5. Don't forget to configure Hudson to look for the coverage.xml file and xmlrunner/**.xml! The directory that outputs nose tests should be in a separate dir from the code coverage, since adding extra XML tests such as JSLint will allow you to use a wildcard on an entire directory to look for JUnit-based reports.

6. If you're using the Hudson extended email-notification, you also need may need to tweak things to deal with the fact that Gmail and other webmail clients may ignore CSS <style> tags:

7. If you want to restrict Nose to look for only your Django-based tests (instead of unittest.TestCase), you can add a Selector:
from django.test import TestCase as DjangoTestCase

class MySelector(Selector):
    def wantClass(self, cls):
        """ Make sure we're searching for a Django TestCase class, not a Python unittest class.
        This overrides the Nose default behavior."""
        return issubclass(cls, DjangoTestCase)

You would then add this line to your file:
NOSE_PLUGINS = ['myapp.test.nose_utils.MySelector',]

8. If you are noticing that the package name outputs are out of order or your conditional branches are not being reported, check out this link:

9. Install the HTML Publisher Hudson plug-in and specify the output location of the HTML dir (specified by the --html-dir-output= dir) inside NOSE_ARGS. Once the HTML Publisher plug-in installed, click on Publish HTML Reports and specify the directory location (you'll need to create the directory too relative to the jobs/ directory path). In this example, I created an xmlrunner/html dir so that the HTML files will all output there.

No comments:

Post a Comment