Archive for August 21st, 2008

Pinocchio –with-spec working!

Thursday, August 21st, 2008

I finally upgraded pinocchio (extensions to nose, hah!) and –with-spec is working.

Here’s the output I currently get from my tests. Now, this is really slick. It generates a “Just good enough” specification based on the automated test cases I’ve created.

Index functional tests
- The response to a GET should have a 200 status code.

Tests for submitted link model object.
- link attributes

Index view should return correct format.
- Content should not be empty.
- Content should contain a list of the top links from the past week.
- Content should have a login/create account link.
- Should return a status code of 200.
- Content should have a submit-link hyperlink.
- Content should have a tag cloud.
- Content should have links for month, day, year, all time and the words “week” for navigation.
- Anonymous users should not see a settings link.
- Should return HttpResponse object.

I’ve run into two slight niggles with it. First, it doesn’t handle methods that are in camel case, only methods that are separated by underscores. Second, it doesn’t dedent docstrings or format them at all. I’m going to get darcs going and submit patches to fix both these problems.

Fixtures with nose

Thursday, August 21st, 2008

So, I’ve finally started working on the database and ran into the biggest problem yet with my nose setup. Specifically, django needs a test database setup before tests of the model object.

To do this, I first tried to find a modern django plugin for nose (with no luck). I then found nose fixtures, which allowed me to setup and teardown the test database for my tests package.

Here’s the __init__.py file for it:

from django.test import utils
from django.conf import settings
from django.db import connection

database_name = None

def setup():
    """Setup the various django fixtures."""
    database_name = settings.DATABASE_NAME
    utils.setup_test_environment()
    connection.creation.create_test_db()

def teardown():
    """Tear down the fixtures and database."""
    connection.creation.destroy_test_db(database_name)
    utils.teardown_test_environment()

Nose test cases

Thursday, August 21st, 2008

So, starting with the previous blog post, I’ve turned the verbal description f the test cases into python code. I also implemented the necessary additions to urls.py and views.py to get them to pass.

Here are my “functional tests” These use the django testing framework, which means it’s pretty close to what a real user would experience. This file needs to be updated after I get some more functionality lower down with unit tests.

functional_tests.py

from django.test import TestCase

class IndexFunctionalTests(TestCase):
    urls = 'linkranking.urls'

    def testIndexReturns200(self):
         """The response to a get should have a 200 status code."""
         response = self.client.get('/')
         self.assertEqual(response.status_code, 200)

test_views.py
This file contains all the real test cases, currently it tests templates, urls and views.

from django.http import HttpRequest, HttpResponse

from linkranking import views

class TestIndexViewLayout():
    """Test the index view so that it returns an HttpResponse object that
    contains:
    * Submit Link hyperlink
    * A tag cloud
    * A list of the top Bayesian ranked links from the past week
    * Links labeled day, month, year, all time
    * A login/create account hyperlink
    """

    def setup(self):
        """Test the index view with an empty HTTP request object."""
        self.response = views.index(HttpRequest())

    def testShouldReturnAnHttpResponse(self):
        """View should return HttpResponse object."""
        assert isinstance(self.response, HttpResponse)

    def testShouldHaveStatusCode200(self):
        """View should return a status code of 200."""
        assert self.response.status_code == 200

    def testResponseShouldNotBeEmpty(self):
        """The response should not be empty."""
        assert self.response.content != ''

    def testShouldHaveSubmitHyperlink(self):
        """The response should have a submit hyperlink."""
        assert '<a href="/submit-link">Submit Link</a>' \
                in self.response.content

    def testShouldHaveTagCloud(self):
        """The response should have a tag cloud."""
        assert '<div id="tag-cloud">' in self.response.content

    def testShouldHaveListOfTopLinks(self):
        """The response should contain a list of the top links from the past
        week."""
        assert '<ul id="top-links">' in self.response.content

    def testShouldHaveLoginLink(self):
        """The response should have a login/create account link."""
        assert '<a href="/login">Login/Create Account</a>' \
                in self.response.content

    def testShouldNotHaveSettingsLink(self):
        """Anonymous users should not see a settings link."""
        assert 'settings' not in self.response.content

    def testShouldHaveTimeNavigationLinks(self):
        """The webpage should have links for month, day, year, all time and the
        words "week" for navigatiion."""
        assert '<a href="/today">today</a>' in self.response.content
        assert 'week' in self.response.content
        assert '<a href="/month">month</a>' in self.response.content
        assert '<a href="/year">year</a>' in self.response.content

So, I’ve generated a lot of very simple test cases to make sure that the correct pieces of the HTML output are there. I’m not sure if this is a good strategy (we’ll see in the future). Luckily these tests are high level enough that they shouldn’t change much when the underlining functionality changes.

One thing I’ve been debating is using BeautifulSoup to look up the tags in the output HTML instead of just poking at it to see if it directly contains strings. Using beautiful soup would definitely be more robust, but I don’t know if it’s necessary.