Wrong provides a general assert method that takes a predicate block. Assertion failure messages are rich in detail. The Wrong idea is to replace all those countless assert\_this, assert\_that, should\_something library methods which only exist to give a more useful failure message than "assertion failed". Wrong replaces all of them in one fell swoop, since if you can write it in Ruby, Wrong can make a sensible failure message out of it.
2. Time Is Money
(Test::Unit)
assert_equal money, time
• verb is moved to front
• direct and indirect objects (expected and
actual) are reversed
3. Time Is Money
(RSpec)
time.should ==(money)
• confusing syntax -- space vs. dot vs.
underscore vs. parens
• == looks familiar, but “should” is inserted
between parameters so it’s not apparent
what the calculation is
4. Time Is Money
(Minitest)
assert time == money
• Ah, now I see what you mean
• Default failure message is not helpful
“Failed assertion, no message given.”
• Making the message helpful violates DRY
assert time == money, “Time should equal money”
5. Time Is Money
(Wrong)
assert { time == money }
• Failure message is helpful:
Expected (time == money), but 6 is not equal to 27
time is 6
money is 27
7. Magic
• RubyParser and Ruby2Ruby by Ryan
Davis
• We turn the block into source code,
parse it into an AST, break it down into
parts, then convert back to code for the
messages
8. Also, we cheat
• We open the source file on disk, jump to
the right line, and parse it
• If you’re constructing your tests with
metaprogramming, you’ve got bigger
problems than not being able to use
Wrong
9. Less Is More
Test::Unit Asserts RSpec Matchers
assert_block { x } x.should be_true
assert(x) x.should be_false
assert_equal x, y
assert_raise LoadError { x } x.should be_nil
assert_raise { x } x.should == y
assert_raise LoadError.new("why") { x } x.should >= y
assert { ... }
assert_raise(RuntimeError, LoadError) { x } x.should be_something(y)
assert_raise_kind_of(LoadError) { x }
assert_instance_of(String, x) x.should be_close(y, delta)
assert_instance_of([String, Fixnum], x) x.should be_instance_of y
assert_nil x x.should be_an_instance_of y
assert_kind_of x.should be_kind_of y
assert_respond_to x, :to_y
assert_match /regex/, s x.should be_a_kind_of y"
assert_same x, y x.should eql(y)
deny { ... }
assert_operator x, :>=, y x.should equal(y)
assert_nothing_raised x x.should exist
flunk
assert_not_same x,y team.should have(11).players
assert_not_equal x,y [1,2,3].should have(3).items
assert_not_nil "this string".should have(11).characters
assert_no_match regex, x x.should have_at_least(number).items
assert_throw
assert_throws x.should have_at_most(number).items
assert_nothing_thrown x.should include(y)
assert_in_delta f, g, delta x.should match(/regex/)
assert_send [o, m, arg1, arg2] lambda { do_something_risky }.should raise_exception
assert_boolean x
assert_true x lambda { do_something_risky }.should raise_exception
assert_false x (PoorRiskDecisionError)
assert_compare x, ">=", y lambda { do_something_risky }.should raise_exception
assert_fail_assertion { x } (PoorRiskDecisionError) { |exception|
assert_raise_message m, { x }
assert_const_defined Test, :Unit exception.data.should == 42 }
assert_not_const_defined Test, :Unit lambda { do_something_risky }.should raise_exception
assert_predicate o, :empty? (PoorRiskDecisionError, ""that was too risky"")
assert_not_predicate lambda { do_something_risky }.should raise_exception
assert_alias_method
assert_path_exist "/foo" (PoorRiskDecisionError, /oo ri/)
assert_path_not_exist "/foo" x.should respond_to(*names)