Ruby Test::Unit sucks (and why I still use it)
evan.tiggerpalace.com
evan.tiggerpalace.com
I already know Ruby's syntax so when I want to query an array's length, I use length (or count or size). I don't want to learn yet another term like "have" and nor do I want to reverse how I normally do this check. have(1).obj vs obj.length are chalk and cheese.
I want my tests to be as close to existing Ruby language constructs and syntax as possible but clean too. Test::Unit keeps us closer to Ruby as Evan demonstrates:
assert_equal 1, i.porkchop_sandwiches.count
But it's not clean enough, IMHO, and you have to learn a bunch of assertion methods to cover basic cases. So I have a "middle way" I picked up from I use RSpec and avoid its more fluffy matchers in preference for something like: i.porkchop_sandwiches.count.should == 1
Now it's more like an "if" condition but you still get the right error message goodness if the assertion fails. It adds the least new syntax to get results using your pre-existing Ruby knowledge. (Disclaimer: I usually just grin and bear Test::Unit for small projects ;-))I MADE A VIDEO WITH FUN MUSIC TO DEMONSTRATE! :D :D
http://www.youtube.com/watch?v=pqc4kxMTCsc
<3 <3 <3 <3 <3
It seems this issue was raised in 2008 and a solution (of sorts) suggested: https://rspec.lighthouseapp.com/projects/5645/tickets/504-wa... .. David Chelimsky seemed to think Ruby 1.9 would lose this warning but it didn't and he reopened the ticket (and it's still open 2 years later) :-) The "check" workaround seems generic and viable, if noisy.
But I thought I'd see what actually happens when you try a super simple example with RSpec itself:
require 'rspec'
describe "a number" do
it "should be equal to itself" do
1.should == 1
end
end
Running with warnings gives some warnings in diff-cls (oops) but none for the 1.should == 1 - the mystery gets a little deeper (RSpec masking it somehow?).UPDATE: I think I found the reason after scouring RSpec 2.0's source. Based on your example but putting should in Kernel rather than Object:
module Kernel
def should; self; end
end
def omg
1.should == 1
end
omg
You get no warning in this case. Curiously, though, if you ditch omg and just run the 1.should == 1 direct, you still get a warning. Confusinger and confusinger.LAST UPDATE: I'm leaving the above for completeness but it appears the result is not such a useful one (see tl's response ;-))
In your RSpec specs, if you only have one "==" expectation and it is the last one of the block, you will not get a warning.
I HAVE MADE ANOTHER AWESOME VIDEO WITH SUPER AWESOME MUSIC TO ILLUSTRATE!!!!
http://www.youtube.com/watch?v=M8SB7mcQfrY
Hope this makes sense! :-D
<3 <3 <3 <3 <3 <3 <3 <3
All that said, though, this is sucky. But since I never run with warnings on, I'm going to put some headphones on and hop around shouting "LA LA LA" whenever I run my specs next.. or I could partake in this grotesque workaround of temporarily forcing warnings off: http://codesnippets.joyent.com/posts/show/1438
i.porkchop_sandwiches.count.should eq(1).
Then for object identity you can still use be: i.porkchop_sandwiches.first.should be(my_sandwich)
This is actually more in line with the original RSpec, which had should_equal and should_be, and I prefer it because you eliminate the warning, and (I think that) "1.should eq(1)" actually speaks better than "1.should == 1" for someone familiar with ruby but new to RSpec.Naturally this is a matter of opinion, but it's testament to your fine work with RSpec (and particularly in the recent modularization work) that it deals deftly with differing opinions without putting anyone at a serious disadvantage.
The other operators are still supported as/is and don't cause the warning that == does.
(thinking about it that's also a good description of TDD vs BDD - TDD is meant to be run, BDD is meant to be read)
In summary, Rspec is on a high horse and thinks I should maintain it like it was another app.
edit: And now I'm wondering if I need a test suite for my test suite. TDD Turtles, all the way down...
is $got, $expected, 'is got eq expected?';
ok $foo, 'foo is true';
like $some_string, qr/foo bar/, 'some string contains foo bar';
cmp_ok $got, '<', $expected, 'got is less than expected';
No need to learn some new language. It's just Perl. (The only confusing part is whether it's $got, $expected or $expected, $got. But I guess that part is arbitrary, like calling "ok" "ok" instead of n"is_the_first_argument_some_sort_of_true_value".)I honestly expect a lot of cpan modules to have some tests that pass only because they don't run on a clean environment since I found some cases like that already.
Finally, I am trying to fix this problem with: http://github.com/jrockway/eval-clean. It basically gives you an object that is a PerlInterpreter object, completely isolated inside. The only issue is keeping the lexical scope between eval calls, and I've almost figured out how to do this. (No, Lexical::Persistence is not even close to an acceptable solution :)
context "parent" do
subject { Factory(:something, :value => other_thing) }
let(:other_thing) { "Value" }
it "has 'Value' value" do
subject.value.should == "Value"
end
context "nested" do
let(:other_thing) { "Totally other value" }
it "has 'Totally other value' value" do
subject.value.should == 'Totally other value'
end
end
endContext is king.
BTW, while I may beat up on RSpec, I learned much of how I perceive TDD indirectly from David Chelimsky. I may not be an RSpec fan any more but I'm a huge David fan.
In addition to nested contexts, it also lets you provide your test name as a string rather than a method name, to avoid wearing out your underscore key with "def test_new_instance_should_respond_to_foo".
[1] https://github.com/citrusbyte/contest/blob/master/lib/contes...
And that's good enough for me.
This does not mean they "suck."