Wednesday, February 23, 2011

Setting up Nose for Django and Hudson

1.
pip install coverage
pip install nose
pip install django_nose
pip install nose-exclude
pip install git+git://github.com/cmheisel/nose-xcover.git#egg=nosexcover

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

django_nose/management/commands/test.py
TestRunner = get_runner(settings)

if hasattr(TestRunner, 'options'):
    extra_options = TestRunner.options
else:
    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 settings.py 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 tests.py 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')
NOSE_ARGS = NOSE_ARGS + HUDSON_NOSE_ARGS

You should change --cover-package accordingly.

4. Your Hudson test command would be something similar to the following
./manage.py 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:

http://hustoknow.blogspot.com/2011/02/using-hudsons-email-ext.html

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

http://packages.python.org/nose/doc_tests/test_selector_plugin/selector_plugin.html.
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 settings.py 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:

http://hustoknow.blogspot.com/2011/03/coveragepy-xml-outputs-in-random-order.html

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