| blackjml ( @ 2006-05-24 14:15:00 |
Test Driven Development with Athena
The boffins at Divmod have produced a fine two-way AJAX toolkit called Athena. To play with it, you'll need to configure Nevow on your coding box. The link should have instructions. If not, Someone should do Something about it.
This post is for people like me who have forgotten how to program without writing unit tests first. Kent Beck's book shows why this is good state to be in.
The Divmod guys have written a unit testing framework called nit that makes it fairly easy to write unit tests for your JavaScript code. This isn't anything new. nit also makes it easy to write unit tests for your Athena widgets, which is awesome. Here's how to do it.
So, in nevow/plugins/foopackage.py:
In foo/test/livetest_foo.py:
In foo/test/livetest_foo.js:
$ nit foo
2006/05/24 11:26 EST [-] Log opened.
2006/05/24 11:26 EST [-] nevow.appserver.NevowSite starting on 8080
2006/05/24 11:26 EST [-] Starting factory <nevow.appserver.NevowSite instance at 0x1427990>
Hit http://localhost:8080/ and you'll see your test and a button. Hit the button. You'll see a big red traceback that shows that your test FAILS.
We have a single, failing test. However, it only does JavaScript stuff, which is boring.
4 Write A Single, Failing Widget Test
To test a widget, we need to create it and render it as a part of the test. Creating a widget requires writing a JavaScript part and a Python part. We'll also need to modify our test to actually call things on the widget.
So, first, change livetest_foo.py:
The boffins at Divmod have produced a fine two-way AJAX toolkit called Athena. To play with it, you'll need to configure Nevow on your coding box. The link should have instructions. If not, Someone should do Something about it.
This post is for people like me who have forgotten how to program without writing unit tests first. Kent Beck's book shows why this is good state to be in.
The Divmod guys have written a unit testing framework called nit that makes it fairly easy to write unit tests for your JavaScript code. This isn't anything new. nit also makes it easy to write unit tests for your Athena widgets, which is awesome. Here's how to do it.
1 Set up files and directories
Create the following files and directories.- foo/__init__.py
- foo/test/__init__.py
- foo/test/livetest_foo.js
- foo/test/livetest_foo.py
- foo/static/js/foo.js
- nevow/plugins/foopackage.py
2 Set up namespaces
Athena lets you put JavaScript files in namespaces, just like Python. However, unlike Python, you have to explicitly map namespaces to files.So, in nevow/plugins/foopackage.py:
from twisted.python import utilThis registers foo/static/js/foo.js as the 'Foo' namespace and foo/test/livetest_foo.js as the 'Foo.Tests' namespace.
from nevow import athena
import foo
def _f(*sib):
return util.sibpath(foo.__file__, '/'.join(sib))
fooPkg = athena.JSPackage({
'Foo': _f('static', 'js', 'foo.js'),
'Foo.Tests': _f('test', 'livetest_foo.js')})
3 Write A Single, Failing Test
Your test needs to have two parts: a JavaScript part and a Python part.In foo/test/livetest_foo.py:
from nevow import loaders, tagsThis is the hook for nit to catch on to your test. nit will render OurFirstTest as a LiveFragment and bind it to an instance of Foo.Tests.OurFirstTest.
from nevow.livetrial import testcase
class OurFirstTest(testcase.TestCase):
jsClass = u'Foo.Tests.OurFirstTest'
docFactory = loaders.stan(tags.div(render=tags.directive('liveTest'))['OurFirstTest'])
In foo/test/livetest_foo.js:
// import Nevow.Athena.TestThis is a weird way of writing JavaScript. Emacs hates it – it does the indentation all wrong, and no one at Divmod had a fix on hand. Anyway, it's all part of the desperate bid to turn JavaScript into Python. The run method gets called by nit when you click 'Run'. Observe!
Foo.Tests.OurFirstTest = Nevow.Athena.Test.TestCase.subclass('OurFirstTest');
Foo.Tests.OurFirstTest.methods(
function run(self) {
self.assertEquals('apple', 'orange');
});
$ nit foo
2006/05/24 11:26 EST [-] Log opened.
2006/05/24 11:26 EST [-] nevow.appserver.NevowSite starting on 8080
2006/05/24 11:26 EST [-] Starting factory <nevow.appserver.NevowSite instance at 0x1427990>
Hit http://localhost:8080/ and you'll see your test and a button. Hit the button. You'll see a big red traceback that shows that your test FAILS.
We have a single, failing test. However, it only does JavaScript stuff, which is boring.
4 Write A Single, Failing Widget Test
To test a widget, we need to create it and render it as a part of the test. Creating a widget requires writing a JavaScript part and a Python part. We'll also need to modify our test to actually call things on the widget.So, first, change livetest_foo.py:
from nevow import loaders, tagsThis creates the widget Bar and renders it as part of the test. However, before we implement the widget, let's finish writing the test: that is, the JavaScript part.
from nevow.livetrial import testcase
from foo import widgets
class OurFirstTest(testcase.TestCase):
jsClass = u'Foo.Tests.OurFirstTest'
docFactory = loaders.stan(
tags.div(render=tags.directive('liveTest'))[
tags.invisible(render=tags.directive('barwidget'))])
def render_barwidget(self, ctx, data):
widget = widgets.Bar()
widget.setFragmentParent(self)
return widget
// import FooAt this point, 'nit foo' will fail saying it can't import foo/widgets.py. Creating foo/widgets.py will let it run properly, but the page will render a stack trace indicating that widgets.Bar doesn't exist. Let's create it minimally. Put this in foo/widgets.py
// import Nevow.Athena.Test
Foo.Tests.OurFirstTest = Nevow.Athena.Test.TestCase.subclass('First');
Foo.Tests.OurFirstTest.methods(
function run(self) {
var bar = self.childWidgets[0];
self.assertEquals(bar.frotz(), 42);
});
from nevow import loaders, tags, athenaNow we can view the tests. However, running them will produce a failure, saying that 'bar' has no properties, referring to the bar in Foo.Tests.OurFirstTest.run. This is because we haven't yet written the JavaScript side of the widget. We've said the class is Foo.Bar, so we'll need to dump it in the Foo namespace which we've configured in foo/static/js/foo.js. Here it is.
class Bar(athena.LiveFragment):
jsClass = u'Foo.Bar'
docFactory = loaders.stan(
tags.div(render=tags.directive('liveFragment'))['Bar'])
// import Nevow.AthenaNow, run 'nit foo' again, hit your localhost and run the tests. You should see "Error: Test Failure: undefined != 42" at the top of your red traceback. You now have a single failing unit test for an Athena widget.
Foo.Bar = Nevow.Athena.Widget.subclass('Foo.Bar');
Foo.Bar.methods(
function frotz(self) {
});
- The code for Step 3
- The code for Step 4