Monday, February 2, 2009

organizing django unit tests

I've had a revelation regarding my unit test organization.

Until recently, I was creating test case subclasses along module boundaries, and defining large numbers of tests within each case to exercise the functionality of that module. For example:


class ModelTestCase(TestCase):
def test_some_model_behavior(self):
MyModel.objects.create(name='foo')

def test_some_other_model_behavior(self):
foo = MyModel.objects.get(pk=1)
MyOtherModel.objects.create(other=foo)


Now, we've ensured that all of the setup for ModelTestCase is performed before OtherModelTestCase, and we can safely sequence each step of a process while testing each step without a lot of copy and paste code. Admittedly, super is kind of tedious, but that is another post on another day.

This worked ok for a while, but eventually, my applications would grow to the point where the cases are not simple gets and creates, but rather entire stories about how a potential user navigates the site.

Consider unit testing a user registration and login, for example. Each of the steps, from getting the registration form to activating the account, and finally logging in needs testing. The real headache comes with trying to organize all those tests so they run in the correct order. One, certainly valid, approach would be to simply put the entire story in a single test. However, this left me repeating certain setup code, and if there's one thing for which I have no patience, it is violations of the DRY principle.

My solution, subclassing test cases to enforce order.


class ModelTestCase(TestCase):
def setUp(self):
self.foo=MyModel.objects.create(name='foo')

def runTest(self):
self.assertEqual(self.foo.name, 'foo')

class OtherModelTestCase(ModelTestCase):
def setUp(self):
super(OtherModelTestCase, self).setUp()
self.bar=self.foo.bar.latest()

def runTest(self):
self.failUnless(self.bar)


Now, I am assured that the ModelTestCase setup has been performed before OtherModelTestCase is run. This allows me to easily sequence steps of a process (like user registration for example, view, submit, email, activate) without having to repeat a lot of code. Admittedly, having to call super all over the place is tedious, but that is another post on another day.

Sunday, February 1, 2009

think twice before threading

I know multithreaded applications are all the rage these days, but I would like to encorage developers to give a little thought to their application design before throwing more threads at the problem. For example, if your application only needs to do a couple (or even several, or maybe even several hundred) things per second, you probably don't need threads.

Now admittedly, I don't know what you're application does or needs as far as threading goes. But, I do know that I've seen more than a couple of apps lately that claim to require more and more threads complete their work in a timely manner, yet shouldn't require more than a single thread.

simple loops in django templates

Every once in a while, I need to repeat some block of html in a django template a fixed number of times. Ideally, I'd just be able to use python's range function. But, django templates don't allow raw python (probably for the best, once you start coding in your template, its a slippery slope). Anyway, for these simple cases, I finally figured out an easy solution:


{% for i in 123|make_list %}
{{ i }}
{% endfor %}

Saturday, January 17, 2009

git project deployment with capistrano

I just sent a patch to the capistrano mailing list to update the git scm class to perform true exports using git archive rather than cloning the whole repository and then removing the git files after checkout. The main advantage of this is the elimination of the unnecessary transfer of all those git objects. However, an added feature is that git archive allows exporting specific paths from the repository, which appears to be a feature requested from time to time. With the above patch, one can configure capistrano like this:


set :scm, :git # of course
set :deploy_via, :export
set :project, [insert-your-repo-subdir-here]


Which will cause capistrano to execute something along the lines of


git archive --format=tar --remote=[your-repo-path] \
master:[your-subdir-path]


to deploy your application, cool huh? The catch, well, not all repositories (notably github) support the archive command via the git protocol, but if you're hosting your own repository or connecting by ssh, this will work just fine.

Thursday, July 24, 2008

marathon runners

It occurred to me this morning:

The difference between the average runner and a marathon runner is that on off days the average runner is off, but the marathon runner only runs five miles.


By the way, I'm still in the first group, but trying to get to the second. This little quip occurred to me while running in the rain this morning and hating every minute of it. I usually find running to be very enjoyable, but today I just felt off and every step seemed to take a monumental effort. Anyway, since my goal is to be run a marathon, I figured I'd better forge on.


I think this anecdote is more generally applicable, imagine if you got "five miles worth" done on your off days in other aspects of life too. How great would your "on" days be!

Wednesday, July 23, 2008

introducing jsqueeze

Not that I want to get into any js framework debates, but I've been using prototype and scriptaculous for my ajax ui toolkit recently. However, I do have to give a nod to the dojo team for their javascript compressor, shrink safe. It seems to me that any javascript compressor/obfuscator must know about the underlying structure of the language if it wants to save every last byte.

As much as I like the idea of shrink safe, I have to confess that patching the rhino javascript process doesn't seem to most elegant way to harness its power. So, with that in mind, I managed to adapt the shrink safe code to simply use the rhino api to accomplish the same task. I've called by javascript compressor jsqueeze (or jsqz for short).

Anyway, if your interested and want to give it a spin, you can download the source here.

autotools mono integration

In short:

configure.ac:

AC_ARG_VAR([MCS], [the Mono C# compiler])

PKG_CHECK_EXISTS([mono])

MONO_PATH="`$PKG_CONFIG --variable=exec_prefix mono`/bin"

AC_PATH_PROG([MCS], [gmcs], [AC_MSG_ERROR([not found])], [$MONO_PATH])


Makefile.am:

EXEEXT=.dll

LINK=$(MCS) -target:library -out:$@

bin_PROGRAMS=foo

foo_SOURCES=foo.cs

foo_LDADD=$(foo_SOURCES)