Permalink
Browse files

Add be_within(delta).of(expected) matcher

- Delegate be_close(expected, delta) to be_within
- Deprecate be_close(expected, delta)
- Reads much better.
- The argument ordering is clearer.
- Closes #32.
  • Loading branch information...
1 parent 451de05 commit 7852dbd08d83d0e09c9481dda2372ee71d29a363 @myronmarston myronmarston committed with dchelimsky Oct 16, 2010
View
43 features/matchers/be_within.feature
@@ -0,0 +1,43 @@
+Feature: be_within matcher
+
+ Normal equality expectations do not work well for floating point values.
+ Consider this irb session:
+
+ > radius = 3
+ => 3
+ > area_of_circle = radius * radius * Math::PI
+ => 28.2743338823081
+ > area_of_circle == 28.2743338823081
+ => false
+
+ Instead, you should use the be_within matcher to check that the value
+ is within a delta of your expected value:
+
+ area_of_circle.should be_within(0.1).of(28.3)
+
+ Note that the difference between the actual and expected values must be
+ smaller than your delta; if it is equal, the matcher will fail.
+
+ Scenario: basic usage
+ Given a file named "be_within_matcher_spec.rb" with:
+ """
+ describe 27.5 do
+ it { should be_within(0.5).of(27.9) }
+ it { should be_within(0.5).of(27.1) }
+ it { should_not be_within(0.5).of(28) }
+ it { should_not be_within(0.5).of(27) }
+
+ # deliberate failures
+ it { should_not be_within(0.5).of(27.9) }
+ it { should_not be_within(0.5).of(27.1) }
+ it { should be_within(0.5).of(28) }
+ it { should be_within(0.5).of(27) }
+ end
+ """
+ When I run "rspec be_within_matcher_spec.rb"
+ Then the output should contain all of these:
+ | 8 examples, 4 failures |
+ | expected 27.5 not to be within 0.5 of 27.9 |
+ | expected 27.5 not to be within 0.5 of 27.1 |
+ | expected 27.5 to be within 0.5 of 28 |
+ | expected 27.5 to be within 0.5 of 27 |
View
1 lib/rspec/matchers.rb
@@ -177,6 +177,7 @@ module Matchers
require 'rspec/matchers/be_close'
require 'rspec/matchers/be_instance_of'
require 'rspec/matchers/be_kind_of'
+require 'rspec/matchers/be_within'
require 'rspec/matchers/change'
require 'rspec/matchers/eq'
require 'rspec/matchers/eql'
View
19 lib/rspec/matchers/be_close.rb
@@ -10,23 +10,8 @@ module Matchers
#
# result.should be_close(3.0, 0.5)
def be_close(expected, delta)
- Matcher.new :be_close, expected, delta do |_expected_, _delta_|
- match do |actual|
- (actual - _expected_).abs < _delta_
- end
-
- failure_message_for_should do |actual|
- "expected #{_expected_} +/- (< #{_delta_}), got #{actual}"
- end
-
- failure_message_for_should_not do |actual|
- "expected #{_expected_} +/- (< #{_delta_}), got #{actual}"
- end
-
- description do
- "be close to #{_expected_} (within +- #{_delta_})"
- end
- end
+ RSpec.deprecate("be_close(#{expected}, #{delta})", "be_within(#{delta}).of(#{expected})")
+ be_within(delta).of(expected)
end
end
end
View
40 lib/rspec/matchers/be_within.rb
@@ -0,0 +1,40 @@
+module RSpec
+ module Matchers
+ # :call-seq:
+ # should be_within(delta).of(expected)
+ # should_not be_within(delta).of(expected)
+ #
+ # Passes if actual == expected +/- delta
+ #
+ # == Example
+ #
+ # result.should be_within(0.5).of(3.0)
+ def be_within(delta)
+ Matcher.new :be_within, delta do |_delta_|
+ chain :of do |_expected_|
+ @_expected = _expected_
+ end
+
+ match do |actual|
+ unless @_expected
+ raise ArgumentError.new("You must set an expected value using #of: be_within(#{_delta_}).of(expected_value)")
+ end
+ (actual - @_expected).abs < _delta_
+ end
+
+ failure_message_for_should do |actual|
+ "expected #{actual} to #{description}"
+ end
+
+ failure_message_for_should_not do |actual|
+ "expected #{actual} not to #{description}"
+ end
+
+ description do
+ "be within #{_delta_} of #{@_expected}"
+ end
+ end
+ end
+ end
+end
+
View
50 spec/rspec/matchers/be_close_spec.rb
@@ -1,49 +1,21 @@
require 'spec_helper'
+
module RSpec
module Matchers
describe "[actual.should] be_close(expected, delta)" do
- it "matches when actual == expected" do
- be_close(5.0, 0.5).matches?(5.0).should be_true
- end
- it "matches when actual < (expected + delta)" do
- be_close(5.0, 0.5).matches?(5.49).should be_true
- end
- it "matches when actual > (expected - delta)" do
- be_close(5.0, 0.5).matches?(4.51).should be_true
- end
- it "does not match when actual == (expected - delta)" do
- be_close(5.0, 0.5).matches?(4.5).should be_false
- end
- it "does not match when actual < (expected - delta)" do
- be_close(5.0, 0.5).matches?(4.49).should be_false
- end
- it "does not match when actual == (expected + delta)" do
- be_close(5.0, 0.5).matches?(5.5).should be_false
- end
- it "does not match when actual > (expected + delta)" do
- be_close(5.0, 0.5).matches?(5.51).should be_false
- end
- it "provides a failure message for should" do
- #given
- matcher = be_close(5.0, 0.5)
- #when
- matcher.matches?(5.51)
- #then
- matcher.failure_message_for_should.should == "expected 5.0 +/- (< 0.5), got 5.51"
+ before(:each) do
+ RSpec.stub(:warn)
end
- it "provides a failure message for should tno" do
- #given
- matcher = be_close(5.0, 0.5)
- #when
- matcher.matches?(5.49)
- #then
- matcher.failure_message_for_should_not.should == "expected 5.0 +/- (< 0.5), got 5.49"
+ it "delegates to be_within(delta).of(expected)" do
+ should_receive(:be_within).with(0.5).and_return( be_within_matcher = stub )
+ be_within_matcher.should_receive(:of).with(3.0)
+ be_close(3.0, 0.5)
end
- it "provides a description" do
- matcher = be_close(5.0, 0.5)
- matcher.matches?(5.1)
- matcher.description.should == "be close to 5.0 (within +- 0.5)"
+
+ it "prints a deprecation warning" do
+ RSpec.should_receive(:warn).with(/please use be_within.*instead/)
+ be_close(3.0, 0.5)
end
end
end
View
64 spec/rspec/matchers/be_within_spec.rb
@@ -0,0 +1,64 @@
+require 'spec_helper'
+
+module RSpec
+ module Matchers
+ describe "[actual.should] be_within(delta).of(expected)" do
+ it "matches when actual == expected" do
+ be_within(0.5).of(5.0).matches?(5.0).should be_true
+ end
+
+ it "matches when actual < (expected + delta)" do
+ be_within(0.5).of(5.0).matches?(5.49).should be_true
+ end
+
+ it "matches when actual > (expected - delta)" do
+ be_within(0.5).of(5.0).matches?(4.51).should be_true
+ end
+
+ it "does not match when actual == (expected - delta)" do
+ be_within(0.5).of(5.0).matches?(4.5).should be_false
+ end
+
+ it "does not match when actual < (expected - delta)" do
+ be_within(0.5).of(5.0).matches?(4.49).should be_false
+ end
+
+ it "does not match when actual == (expected + delta)" do
+ be_within(0.5).of(5.0).matches?(5.5).should be_false
+ end
+
+ it "does not match when actual > (expected + delta)" do
+ be_within(0.5).of(5.0).matches?(5.51).should be_false
+ end
+
+ it "provides a failure message for should" do
+ #given
+ matcher = be_within(0.5).of(5.0)
+ #when
+ matcher.matches?(5.51)
+ #then
+ matcher.failure_message_for_should.should == "expected 5.51 to be within 0.5 of 5.0"
+ end
+
+ it "provides a failure message for should not" do
+ #given
+ matcher = be_within(0.5).of(5.0)
+ #when
+ matcher.matches?(5.49)
+ #then
+ matcher.failure_message_for_should_not.should == "expected 5.49 not to be within 0.5 of 5.0"
+ end
+
+ it "provides a description" do
+ matcher = be_within(0.5).of(5.0)
+ matcher.matches?(5.1)
+ matcher.description.should == "be within 0.5 of 5.0"
+ end
+
+ it "raises an error if no expected value is given" do
+ matcher = be_within(0.5)
+ expect { matcher.matches?(5.1) }.to raise_error(ArgumentError, /must set an expected value using #of/)
+ end
+ end
+ end
+end

0 comments on commit 7852dbd

Please sign in to comment.