I was going to write about Ruby and SQL, but I’m having fun doing other Ruby-related stuff this afternoon, so I’ll write about that instead.
I was writing this unit test, for a class
DeveloperWriter. And I got tired of typing
DeveloperWriter.new("arg") all the time. (Actually, I got tired of typing
new DeveloperWriter("arg") and then being reminded that that isn’t valid Ruby, but never mind that.) So I added a
create function to my test class which calls the appropriate new for me: I save fifteen keystrokes each time, and after a few object creations, I’m ahead.
And then I wrote tests for another class, and did the same thing. The third time, this got sort of boring. Hey, maybe I can use reflection to do this for me in a generic fashion? I’m using consistent names for my tests, I should be able to get my hands on the class under test?
So I needed some sort of testcase helper class. Or helper module? Let’s go with class: it can inherit from
Test::Unit::TestCase as well, getting rid of that duplication as well. Alas, a bit of experimentation showed that that doesn’t work, or at least not easily: if your actual tests inherit from some intermediate class which inherits from
TestCase, then the magic test runner stuff doesn’t work. I took a look through the source code, but that didn’t enlighten me: I couldn’t figure out how the magic stuff works at all, and to the extent that I can figure things out, it doesn’t seem like there’s an obvious workaround. So I’ll save that for a later possible improvement.
Mixin module it is, then. I hope I’ll figure out soon when you put utility functionality in a class (to be inherited from) and when you put it in a module (to be mixed in). The next step: can I get my hands on the
Class object representing the class under test? A bit of playing around with
irb (yay interactive interpreters, it’s been a while since I used one for anything other than Emacs Lisp) suggests that I should be able to find the name of the class under test by doing
self.class.name.sub(/^Dbcdb::Test/, ""), and get the test class itself by doing
::Dbcdb.const_get(self.class.name.sub(/^Dbcdb::Test/, "")). (The regexp usage seems a bit overkill, but it actually paid for itself pretty quickly by catching some errors that would have been more confusing if I hadn’t had a
nil floating around. Still, I’m surprised there isn’t another way to remove an initial substring; I’m probably missing something.)
By then, I was getting into the spirit of things:
new isn’t anything magic, it’s just a method on the corresponding
Class object. And I’ve just found that object, so I can write my generic
create function. And hey, it works!
In a fit of doubtless premature optimization, I decided it seems a bit silly to look up the class object over and over again: can’t I stash that in a member variable? It’s apparently considered a bit gauche for modules to have member variables, but it is possible. But where do I initialize it? An
initialize method doesn’t seem to do the right thing. I could just initialize it in the body definition, but when I tried that, the wrong class got looked up. I ended up with an accessor function that caches the value; not my favorite style, but it works. Again, maybe there’s something I’m missing.
And, now that I’ve got this helper module, I can throw in other stuff, too. For example, my tests also each have an
assert_bad function (speaking of bad, that’s a lousy name, I should rename it) that tries to construct an object and tests than an
ArgumentException is thrown. (It’s one line long, as opposed to its five-line Java variant.) So now I can pull that up to my helper class, too. And I have more such ideas in mind.
That was fun, and my tests are more expressive: the test code is almost all assertions, instead of generic helper functions.
Not much else to report yet today. I did get to use regular expressions one other time, replacing 26 lines of Java code with 3 lines of Ruby. (Hmm, maybe I’ll eliminate one of those lines by doing a parallel assignment.) Poking around, there seems to be a
java.util.regex package that could have helped slim down the Java code, but it still wouldn’t have been as nice. And Java just doesn’t encourage you to use regexps with the same abandon. (Not that this is unique to Ruby by any means – it’s just a scripting language thing.)
And I can feel the static typing habits just drain away…
There are no revisions for this post.