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…
Post Revisions:
There are no revisions for this post.