Readable test names in Rails 2.1
I’m currently working on a Rails application. It has some non-trivial business rules, so I ended up writing test methods along the lines of:
-
def test_a_message_with_a_password_protected_channel_as_recipient_will_be_delivered_to_a_users_mailbox_if_that_user_is_subscribed_to_said_channel
Okaaaay….. This is what I think of that method name:

Nice boat!
Other than the mental-psychological stress as well as an unexplainable impending feeling of doom that such a long method name brings forth in our minds, one have to ask oneself whether it is morally justified to unintentionally punish whomever will ever read this code by presenting them with such a NICE BOAT, even if said person deserves it.
As much as I love RSpec, it doesn’t feel totally appropriate to use it, because then everything must start with “it”. Unfortunately not all rules in my application can be described with sentences that start with “it”. Rails edge has a solution though. You can define test methods in a declarative style ala RSpec, like this:
-
test "an anime should be invalid if any of its characters are invalid" do
-
# Your usual test code here.
-
end
After staring at my test cases for the 2^32th time in a futile attempt to understand what the test method names are actually trying to tell me, I gave up and decided that it’s time to dig up a series that I’ve been trying to finish for the past 3 months. Surely only this will save me from going completely insane.

The face of un-insanity (?)
There Chu Yeow, I did what you asked me to do.

Baka baka baka! ….or maybe not.
Anyway, under the hood, the test method translates that block to:
-
def test_an_anime_should_be_invalid_if_any_of_its_characters_are_invalid
That’s nice. But it could be nicer. I don’t want to upgrade to Edge so I decided to copy & paste the ‘test’ method from ActiveSupport edge - it’s only 6 lines. And yesterday I found out that it’s apparently possible for method names to contain arbitrary binary data, except “\0″. So you can do this:
-
Object.send(:define_method, "omg\1wtf\n!@$%^&*()") do
-
"abc"
-
end
-
Object.new.send("omg\1wtf\n!@$%^&*()") # => "abc"
Cool! So this means we can have RSpec-style test method names even when using Test::Unit.
So I modified the test method a little bit. Copy and paste this into your test/test_helper.rb to enjoy this:
-
def self.test(name, &block)
-
test_name = "test: #{name.squish}".to_sym
-
defined = instance_method(test_name) rescue false
-
raise "#{test_name} is already defined in #{self}" if defined
-
define_method(test_name, &block)
-
end
My test method now becomes:
-
test "a message with a password protected channel as recipient will be delivered to a user’s mailbox, if that user is subscribed to said channel" do
-
…
-
end

Geoffrey Grosenbach said,
July 4, 2008 @ 11:42 pm
This is definitely the way to go. If you didn’t discover this until now, you should be reading Jay Fields’ blog (the author of that feature):
http://blog.jayfields.com/
Joshua said,
July 5, 2008 @ 8:31 am
This has been available (along with nested context blocks) via a plugin called Shoulda (http://www.thoughtbot.com/projects/shoulda) for some time now. I’m not sure if the test naming would be to your liking, though.
Anthony Eden said,
July 5, 2008 @ 9:19 am
+1 for Shoulda.
RSL said,
July 5, 2008 @ 12:38 pm
Not that I’m pushing the RSpec crack [well, okay mebbe a little] but I don’t see how any of the test names in your code wouldn’t be shorter using RSpec’s “it” naming standard.
test “an anime should be invalid if any of its characters are invalid” # => it “should be invalid if it has invalid characters”
test “a message with a password protected channel as recipient will be delivered to a user’s mailbox, if that user is subscribed to said channel” # => it “should get delivered to the user if channel is password protected and user is subscribed to channel”
That last one could be even shorter if another test shares the conditionality of the channel being password protected because it could be moved to the describe block. There’s plenty of reasons to not use RSpec though. I just don’t think the naming of the tests is one of them. YMMV.
Andy Waite said,
July 5, 2008 @ 4:49 pm
This (and Shoulda) certainly make things easier on the eyes, but long test names are still awkward to read and comprehend. And if you have a few in the one file that only vary by a word or two, it’s easy to confuse them and edit the wrong method.
Shoulda’s support for contexts might be helpful for this, but I haven’t dug into it much yet.
Hongli said,
July 5, 2008 @ 9:49 pm
RSL: the problem is that “message” is not the subject of the test description: the Mailman class is. So “it” would be totally inappropriate.
Chu Yeow said,
July 6, 2008 @ 6:17 am
Hehe Hongli, I liked Spice and Wolf but kinda dropped Gunslinger Girl 2
Btw, if you aren’t using it yet, MyAnimeList is kick-ass for keeping track of anime (my list is at http://myanimelist.net/animelist/chuyeow).
Aleksandr said,
July 6, 2008 @ 11:59 am
Nice trick. But I don’t like that IDEs don’t recognize these test methods anymore.
RSL said,
July 6, 2008 @ 1:46 pm
@ Hongli. I see but I’m not sure I agree. In RSpec, you wouldn’t be specing the message getting sent in the Mailman class per se so much as the Mailman class sending the mail. [Yes, it’s a semantic difference and that’s somewhat a big deal in RSpec, viz Sapir-Whorf etc.]. Amending my spec to reflect this…
test “a message with a password protected channel as recipient will be delivered to a user’s mailbox, if that user is subscribed to said channel” # => it “should send message to user if channel is password protected and user is subscribed to channel”
I don’t want to sound like I’m arguing to be persnickety. I only mean to point out that RSpec is more than just “clever” phrasing of Test::Unit ideas and that the decision to use it or not shouldn’t just be made on naming conventions.
Pratik said,
July 9, 2008 @ 6:14 pm
Not that I have any love for RSpec, but how hard can it be to alias :it to :should, :test, :shit, :whatever ? I don’t understand the reason to choose your testing library based on the it/test/should/etc. keyword. I think there are better parameters for taking that decision.
Hongli said,
July 9, 2008 @ 6:21 pm
I didn’t feel like installing RSpec. The Test::Unit stubs had already been created so I used them instead.