Archive for the 'django' Category

Automatic regression testing in Django

Monday, January 19th, 2009

Django and python make it a breeze to build and run automatic regression tests. In 1-2 days you can easily build 50 test cases which cover the basic functionality of your application, and make sure no view or model has been broken. The test cases can be divided into three groups:

  • Tests for models
  • Tests for views
  • Tests for various utility functions

Here’s how to do it. You start by adding a tests.py file in your application directory. The Django default test runner will execute the runTest() method for every TestCase derived class when you run manage.py test. Here’s an example tests.py. Django has specific test functions to log you in and out of the standard authentication framework, but this example is using it’s own authentication functionality.

from django.test import *
import doctest

class ViewTests(TestCase):
    def runTest(self):

        c = Client()
        # Try to get private content prior to login
        response = c.get('/projects/')
        self.assertRedirects(response, '/login/?next=/projects/')

        # Attempt login using the wrong password
        response = c.post('/login/',
            {'login': 'eki', 'pass': 'wrongpassword'})
        self.assertContains(response,
            'Please check your user ID and password')

        # Use proper credentials
        response = c.post('/login/?next=/projects/',
            {'login': 'eki', 'pass': 'helloworld'})
        self.assertRedirects(response, '/projects/')

        # Now fetch each of the basic pages
        response = c.get('/projects/')
        self.assertContains(response, 'Example project')
        response = c.get('/users/')
        self.assertContains(response, 'Erkki Tapola')

This is the mechanism for testing views. Next, you can add doctests to your models.py. Doctest is a very handy notation for test cases to be executed from the command line python. Essentially you just run a test case on the command line and cut and paste the command and it’s output to your class or function documentation. For example:


def get_base_dir(path):
    """
    >>> get_base_dir('/first/second')
    'first'
    >>> get_base_dir('first/second')
    'second'
    """
    items = string.split(path, '/')
    return items[1]

The Django test runner runs doctests from models.py automatically, but if you want it to execute doctest cases from other files than models.py as well, you can add a TestCase class to tests.py for that purpose:


class ModuleTests(TestCase):
    def runTest(self):
        def mytestmod(m):
            result = doctest.testmod(m)
            print str(result[1]) + ' test cases ran in ' + str(m)
            self.assertEqual(result[0], 0)

        print "\nRunning test cases for individual modules\n"
        print 80*'*'

        for x in [tracker.main.util, tracker.main.projects]:
            mytestmod(x)

        print 80*'*' + "\n"

When you’re finished with your test cases and able to run them all using manage.py test, you can connect your cool new test suite to svn using the pre-commit hook. This way you can make sure that the code changes are verified prior to commit. Here’s what you do on Linux:

  • Go to hooks/ which is under your SVN repository directory and rename the start-commit.tmpl to start-commit
  • Edit the file, and before the last exit 0, add the line:
    • for x in `find . -iname manage.py -exec dirname {} \;`; do cd $x; if ! ./manage.py test; then exit 1; fi; done

I should probably write this in python to make it platform independent. This unix shell command will find any manage.py from the subdirectories of your current location and use it for running tests. If any manage.py returns an error, the commit will be cancelled. This addition will not influence projects which doesn’t contain a manage.py, but if there is one, then the tests must pass.

A similar hooking mechanism exists in cvs and probably most other version control systems.

You can check out the article on Testing Django
applications
for a comprehensive tutorial.

PHP model for automatic SQL and form creation

Friday, January 16th, 2009

After using Django and it’s hibernate-like Object-Relational Model (ORM) for a month or so, I decided to try how I could create a similar model mechanism using PHP. Here’s an example which I wanted to keep just under 100 lines for light reading. This example code demonstrates how to:

  • Easily create a structure with various types of fields (though currently it only supports CharField)
  • Generate SQL code for table creation
  • Generate a HTML form for editing the information

When you run the example, it will output the following (note the intentional error in the SQL statement syntax):

CREATE TABLE `person` (
`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
`name` VARCHAR(40),
`address` VARCHAR(40),
`phone` VARCHAR(15),
);

<form action=’.’ method=’POST’>
<table>
<tr><th>Name</th><td></td></tr>
<tr><th>Address</th><td></td></tr>
<tr><th>Phone</th><td></td></tr>
</table>
</form>

The code itself is here for you to use in any way you please, as long as you don’t hold me accountable for the results:

download code
run code



<?php

class Field
{
    var $size;
    var $label;
    var $editor;
    var $value;

    function __construct($size = 80, $label = '')
    {
        $this->size = $size;
        $this->label = $label;
    }
}

class CharField extends Field
{
    function __construct($size = 80, $label = '')
    {
        $this->editor = 'input type="text"';
        parent::__construct($size, $label);
    }

    function get_db_type()
    {
        return 'VARCHAR('.$this->size.')';
    }
}

class Model
{
    function get_name()
    {
        return get_class($this);
    }

    function make_create_sql()
    {
        $str = '';
        echo "nCREATE TABLE `",strtolower($this->get_name()),"` (n";
        echo "   `id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,n";

        foreach ( get_class_vars($this->get_name()) as $x => $y)
        {
            echo '   `',$x,'` ',$this->$x->get_db_type(),",n";
        }

        echo ");nn";
    }

    function make_form()
    {
        foreach ( get_class_vars($this->get_name()) as $x => $y)
        {
            echo $this->$x->label,': <',$this->$x->editor,' name="',$x,"\">n";
        }
    }

    function make_form_table()
    {
        echo "  <table class=\"mytable\">n";
        foreach ( get_class_vars($this->get_name()) as $x => $y)
        {
            echo '    <tr><th>',$this->$x->label,'</th><td><',
                $this->$x->editor,' name="',$x,'" value="'.$this->$x->value.
                "\"></td></tr>n";
        }
        echo "  </table>n";
    }
}

class Person extends Model
{
    var $name, $address, $phone;

    function __construct()
    {
        $this->name = new CharField(40, 'Name');
        $this->address = new CharField(40, 'Address');
        $this->phone = new CharField(15, 'Phone');
    }
}

$j = new Person();

$j->name->value = "Erkki Tapola";

$j->make_create_sql();

echo "<form action='.' method='POST'>n";
$j->make_form_table();
echo "</form>n";

?>


You can easily add more field types according to the example CharField. Other features also available in Django, but this example doesn’t (yet) implement:

  • Query mechanisms to abstract SQL SELECT.
  • Persistence; save mechanisms to abstract UPDATE and INSERT.
  • Database table relation, fetch rows based on foreign keys and many-to-many relations

In addition, what I would like to see in Django:

  • Methods to update the current database schema with the added and modified field information. (Note: Check out Django-evolution, which is doing exactly this, and it even maintains a history of modifications.

Such a functionality would be safe for added columns and even column truncation and removal, as long as you indicate that this is actually your intent.