Saturday, December 24, 2005
Signal to Noise Ratio in Rails
Without getting all hysterical about it, there are many things in Rails that should give pause to the J2EE crowd. When I switched from C++ to Java, the thing I liked most about coding in Java was the much improved signal-to-noise ratio compared with C++. I found I spent much less time thinking about computer stuff and much more time thinking about the problem at hand and the concepts of the problem space, largely ignoring the problems of the implementation space. This was partly the language, partly the libraries, and partly the community around Java at the time, but when you add it all up, the result was better software faster, which is really all I want.
Now, having used the classic J2EE stack for a while (Struts/Tiles/Hibernate, et al), and having watched J2EE vendors bloat their products to the point of collapse, using Rails has given me the same feeling. No struts-config.xml, no tiles-defs.xml, no *.hbm.xml files, and much less time thinking about the machine and its implementation layers. The signal-to-noise ratio is simply better than in the form of J2EE I've learned. Ruby/Rails also appear to have the libraries and the community to make the whole stack work. The whole setup feels more supple and malleable than Java, and, since that feeling was what attracted me to Java, that has to be a concern for those who think Java is some kind of universal maximum, rather than just a local one, as all technologies are.
There are some things in Rails about which I retain mixed feelings. ActiveRecord seems to me to be less of an O/R mapping layer, and more of an O/R unification layer. This caused me severe brain ache for a while, but I'm suspending disbelief for the moment. The unit testing model involves database access (and lots of it), which is a definition of unit testing I've always disliked strongly. The functional testing model (so far as I have looked) doesn't use a model based on parsing and filling out and submitting the HTML forms served by the running application. It appears to be more a controller-testing setup than what I would call a functional testing setup. But I'm only a newbie and might have missed some stuff.
Monday, April 12, 2004
The Need for Speed
At the Melbourne eXtreme Programming Enthusiasts Group the other night we had a big rantorama about TDD. One of the points in my presentation was that Kent had said that unit tests had to be fast, and he cited one project in which there were 4,000 tests that ran in about 20 minutes. A few guys from my team were there and guffawed out loud. Our current project (4 months old) has 1,300 unit tests that run in about 10 seconds, so they weren't impressed with Kent's numbers at all. Even allowing for Moore's law over a few years, we'd run about 4,000 tests in 30 seconds, which two speed-doublings ago would take 2 minutes. That's still an order of magnitude faster...so what's the deal?
This led to lots of enthusiastic debate about what the hell a unit test actually is. For what it's worth, here's my simplistic test categorization model:
1. A unit test is a test that requires only the class under test and its immediate dependencies (or mocks thereof). This means no deployment, no packaging, nothing. That's why they're fast.
2. A functional test is a test you can't run until the app is packaged and deployed in some way. So you "in-container unit test" fans aren't doing unit tests by this model. I'm hard on this stuff because anything requiring deployment puts you in a different order of magnitude speed wise; this doesn't mean these tests aren't valuable, just that I don't count them as _unit_ tests, even if they only test a single unit of functionality.
3. An integration test is a test of your external dependencies, not necessarily requiring your app to be deployed at all, but requiring your external dependencies to be available. This is where databases come in, or other services provided by EJB's or anything else. These tests are also usually way too slow for me to want too many of them, and I want to isolate them from my app as much as possible. This means a separate test harness for the external stuff and leave my app just unit testing against the assumptions proven by the integration test suite.
The point is that I want to push unit tests as far as I can because they just provide so much more performance than the other types of tests. That's the only reason. I want as few of the others as possible because I spend enough of my life waiting for computers to get done with stuff as it is. Yes, you _can_ unit test custom tags to within an inch of 100% coverage!
So what's everyone else getting speed-wise???
Sunday, March 7, 2004
Getting into the Swing of TDD
Just came off a tough late night/early morning pairing session with Simon Harris. We're working on a Swing app that will let you wander around the duplication in your code base by reading a Simian log file and referring to the original code tree. We're also trying an experiment where we use TDD and blog the experience so that we can publish the gory details of the construction of the app, on the presumption that anyone cares, of course...:P
Last night's session will make for interesting reading, mainly because we're both pretty clueless about Swing. In keeping with Jon Eaves ideas on spiking and TDD, we mucked around for a while trying to figure out how to use the java.awt.Robot to drive our panels, but we got there in the end. What we are trying to explore is how far we can push TDD, and I, for one, am so sick of web apps that this seemed like a cool idea.
I don't know enough about Swing to understand why a lot of folks seem to hate it these days, but it's always appealed to me as having the potential to facilitate real unit testing. By this I mean that a UI area composed of multiple JPanels should be able to have independently designed and tested each of those panels before assembling the UI and running functional tests over it.
The theory goes something like this:
- you don't have to write tests for one line delegation methods in my book. So if the UI was a veneer so thin over a fully tested set of non-Swing stuff, I'm happy to just glue the UI onto the layer below with trivial delegation. This is the interesting part - how thin can the UI layer be? So thin that no testing is required?
- you use a Mediator to coordinate the interaction between panels (which you of course make an interface so you can easymock it for testing), and each panel implements a Colleague interface (again facilitates testing). This means that although there is 2-way comms between the mediator and the colleagues, either end of this pipe can be mocked, allowing testing of each bit independently.
- you then assemble your UI like any other swing UI and the bits play nice.
We had previously gotten our mediator TDD-ed into existence and were happy with it. We then came to creating the JPanel that contains a list of files that share some duplicated code. This panel was also TDD-ed into existence but with all its behaviour in non-Swing methods. All good. Then it came time to (we thought) just add the JList and off we go. Turns out the amount of code required, with event listeners and a private inner ListModel class left us facing the dreaded NullPointerException at run time. If there is a more glaring signal that your code is crap, I don't know it.
So we came to the conclusion that we would have to actually unit test the UI with events somehow, you know, mouse and keyboard stuff. Yuk. We mucked around for a while trying to figure out how to do this while keeping the test 'headless', but failed dismally. In the end we came to grips with the pixel coordinate stuff required by Robot and plonked our little panel into a JFrame of its own in the unit test and just displayed it and started the Robot clicking on stuff.
So the upshot was that a single panel pops up on the screen and dances about a bit being unit tested against a mock Mediator that catches and verifies the panel's interactions with the rest of the dialog that isn't even there yet. Long story, I know, but I thought it was kinda fun and my blog's been a bit quiet lately.