While working on my first semester project for my CS masters degree, I chose to use GitLab for project management and source code hosting. While I love GitLab for its integrated time tracking, issue board, powerful interface, etc. I really miss one crucial feature: test result reporting. I looked far and wide and found a couple of approaches to facilitate test reporting, none of which satisfied my picky self. And while there is an open issue scheduled for the 10.3 milestone, I could not wait that long. Thus, I decided to throw my hat in the ring and implement what i needed myself.

Django JUnit Reporting

Python is by no means my strongest language. While I would consider myself to be reasonably adept, real Pythonists would most likely prefer I don’t use it. Regardless, the only rapid development web framework i know is Django. While I could have chosen another framework, it would have probably been even harder to get something done.

I wrote the first prototype of my reporting application in about 90 minutes after I decided I was actually going to do it. It looked pretty bad (luckily I didn’t take screenshots :) since it was just a couple of plain old HTML lists. About a day later, I had come up with something that looks like this:

The JUnit Reporting Project View

Clearly I have neither studied web design, nor have am I particularly talented, but I think it suits my needs.

How it Works

Currently, most management tasks have to be accomplished via either the Django Python shell or the Django admin interface. Did I mention I was in a hurry? However once installed (we will get to that momentarily) publishing JUnit reports is very easy:

  curl -X PUT \
    -H "Authorization: Token <your_auth_token>" \
    -H "Content-Disposition: attachement; filename=report.xml" \
    --upload-file some-report.xml \
    https://<your_reporting_host>/p/<your_project_name>/upload/<your_build_id>
Listing 1: Publishing test result using curl

Thats it! A single PUT request is enough to publish a test report. In the background, JUnit Reporting uses junitparser to process the uploaded file and take it apart into suites, test, failures, and errors. All of the building blocks of a JUnit XML file are available in JUnit Reporting, so you could easily extend it to your liking.

Getting Started

If you are familiar with Django, installing JUnit Reporting is fairly simple. Note however, that there is currently no package for JUnit Reporting in the PyPI since it is pretty much the result of a 24h hack session.

Installation

While there is no package on PyPI, you can still use pip to install JUnit Reporting like so:

  pip install git+https://github.com/fmorgner/django-junit-reporting
Listing 2: Installing JUnit Reporting from Github

pip will also take care of installing all of JUnit Reporting’s dependencies for you, so installation is really a piece of cake. Besides the Python parts, JUnit Reporting also needs less.js to compile its style sheet. Most modern Linux distributions ship less.js in some form. However, if your distro does not, here is a simple npm command to install less.js:

npm install -g less
Listing 3: Installing less.js via NPM

Installing JUnit Reporting ‘into’ Your Django Project

Similarly, adding the JUnit Reporting to your Django project is easy as well. First make sure to add the required entries to your INSTALLED_APPS setting:

  INSTALLED_APPS = [
      #  other applications ...
      'bootstrap3',
      'font_awesome',
      'junit_reporting',
      'rest_framework',
      'rest_framework.authtoken',
      'static_precompiler',
  ]
Listing 4: Adding JUnit Reporting to INSTALLED_APPS

Afterwards, you should make sure that you add the static precompiler to your STATICFILES_FINDERS setting, and that you set the STATIC_ROOT setting of your project:

  STATICFILES_FINDERS = (
        'django.contrib.staticfiles.finders.FileSystemFinder',
        'django.contrib.staticfiles.finders.AppDirectoriesFinder',
        'static_precompiler.finders.StaticPrecompilerFinder',
  )
  STATIC_URL = '/static/'
  STATIC_ROOT = os.path.join(BASE_DIR, 'static')
Listing 5: Setting up Static File Handling

Next, you need to make sure that token authorization is activated for Django REST framework, since the upload “API” requires those tokens:

  REST_FRAMEWORK = {
      'DEFAULT_PERMISSION_CLASSES': (
          'rest_framework.permissions.IsAuthenticated',
      ),
      'DEFAULT_AUTHENTICATION_CLASSES': (
          'rest_framework.authentication.TokenAuthentication',
      )
  }
Listing 6: Enabling Django REST framework Token Authorization

As the (almost) last step, you will need to hook up the URLConf of JUnit Reporting to your Django project. To make the application reachable via ‘/reporting’ you could use the following code in you application’s urls.py:

  from django.conf.urls import include
  #  ...
  urlpatterns = [
    #  other URLs ...
    url(r'^reporting', include('junit_reporting.urls')),
  ]
Listing 7: Hooking up The URLconf

Of course you will still need to apply JUnit Reporting’s migrations to your project like so:

  ./manage.py migrate
Listing 8: Hooking up The URLconf

Now you are ready to get started publishing your test results. As mentioned in the beginning, most management tasks are still hidden behind the Django python shell or the Django admin interface, consider it a prototype. You will need to create users, tokens, and at least one project to really get going, but that should be trivial.

Summary

It's alive!!

Granted, this solution is still far from perfect. However, I feel it is simple(ish) to set up and it should work fine for small to medium projects. I hope to extend it in the future and I would very much appreciate your feedback and participation if you find JUnit Reporting lacking in some aspects (I promise you will :).