Skip to content

Commit

Permalink
Merge pull request #405 from sferik/be_between
Browse files Browse the repository at this point in the history
Add RSpec::Matchers::BuiltIn::BeBetween
  • Loading branch information
myronmarston committed Jan 4, 2014
2 parents 8c88684 + 7bbe151 commit 4f44ec9
Show file tree
Hide file tree
Showing 8 changed files with 172 additions and 18 deletions.
22 changes: 15 additions & 7 deletions .travis.yml
@@ -1,5 +1,10 @@
script: "script/test_all"
before_install:
- gem update bundler
- bundle --version
- gem update --system 2.1.11
- gem --version
bundler_args: "--standalone --binstubs --without documentation"
script: "script/test_all"
rvm:
- 1.8.7
- 1.9.2
Expand All @@ -8,10 +13,13 @@ rvm:
- 2.1.0
- jruby-18mode
- jruby-19mode
- ree
- jruby-head
- rbx
before_install:
- gem update bundler
- bundle --version
- gem update --system 2.1.11
- gem --version
- ree
- ruby-head
matrix:
allow_failures:
- rvm: jruby-head
- rvm: ruby-head
fast_finish: true

15 changes: 15 additions & 0 deletions lib/rspec/matchers.rb
Expand Up @@ -317,6 +317,21 @@ def be_a_kind_of(expected)
alias_method :be_kind_of, :be_a_kind_of
alias_matcher :a_kind_of, :be_a_kind_of

# Passes if actual.between?(min, max). Works with any Comparable object,
# including String, Symbol, Time, or Numeric (Fixnum, Bignum, Integer,
# Float, Complex, and Rational).
#
# @note Inclusive of both min and max values.
#
# @example
#
# expect(5).to be_between(1, 10)
# expect(11).not_to be_between(1, 10)
def be_between(min, max)
BuiltIn::BeBetween.new(min, max)
end
alias_matcher :a_value_between, :be_between

# Passes if actual == expected +/- delta
#
# @example
Expand Down
11 changes: 5 additions & 6 deletions lib/rspec/matchers/built_in.rb
Expand Up @@ -3,14 +3,15 @@
module RSpec
module Matchers
module BuiltIn
autoload :BeAKindOf, 'rspec/matchers/built_in/be_kind_of'
autoload :BeAnInstanceOf, 'rspec/matchers/built_in/be_instance_of'
autoload :BeBetween, 'rspec/matchers/built_in/be_between'
autoload :Be, 'rspec/matchers/built_in/be'
autoload :BeTruthy, 'rspec/matchers/built_in/be'
autoload :BeComparedTo, 'rspec/matchers/built_in/be'
autoload :BeFalsey, 'rspec/matchers/built_in/be'
autoload :BeNil, 'rspec/matchers/built_in/be'
autoload :BeComparedTo, 'rspec/matchers/built_in/be'
autoload :BePredicate, 'rspec/matchers/built_in/be'
autoload :BeAKindOf, 'rspec/matchers/built_in/be_kind_of'
autoload :BeTruthy, 'rspec/matchers/built_in/be'
autoload :BeWithin, 'rspec/matchers/built_in/be_within'
autoload :Change, 'rspec/matchers/built_in/change'
autoload :Compound, 'rspec/matchers/built_in/compound'
Expand All @@ -33,11 +34,9 @@ module BuiltIn
autoload :StartWith, 'rspec/matchers/built_in/start_and_end_with'
autoload :ThrowSymbol, 'rspec/matchers/built_in/throw_symbol'
autoload :YieldControl, 'rspec/matchers/built_in/yield'
autoload :YieldSuccessiveArgs, 'rspec/matchers/built_in/yield'
autoload :YieldWithArgs, 'rspec/matchers/built_in/yield'
autoload :YieldWithNoArgs, 'rspec/matchers/built_in/yield'
autoload :YieldSuccessiveArgs, 'rspec/matchers/built_in/yield'
end
end
end


40 changes: 40 additions & 0 deletions lib/rspec/matchers/built_in/be_between.rb
@@ -0,0 +1,40 @@
module RSpec
module Matchers
module BuiltIn
class BeBetween < BaseMatcher
include Composable

def initialize(min, max)
@min, @max = min, max
end

def matches?(actual)
@actual = actual
comparable? and @actual.between?(@min, @max)
end

def failure_message
"expected #{@actual.inspect} to #{description}#{not_comparable_clause}"
end

def failure_message_when_negated
"expected #{@actual.inspect} not to #{description}"
end

def description
"be between #{@min.inspect} and #{@max.inspect} (inclusive)"
end

private

def comparable?
@actual.respond_to?(:between?)
end

def not_comparable_clause
", but #{@actual.inspect} does not respond to `between?`" unless comparable?
end
end
end
end
end
5 changes: 2 additions & 3 deletions lib/rspec/matchers/built_in/be_within.rb
Expand Up @@ -33,7 +33,7 @@ def failure_message
end

def failure_message_when_negated
"expected #{@actual} not to #{description}"
"expected #{@actual.inspect} not to #{description}"
end

def description
Expand All @@ -51,8 +51,7 @@ def needs_expected
end

def not_numeric_clause
return "" if numeric?
", but it could not be treated as a numeric value"
", but it could not be treated as a numeric value" unless numeric?
end
end
end
Expand Down
8 changes: 8 additions & 0 deletions spec/rspec/matchers/aliases_spec.rb
Expand Up @@ -80,6 +80,14 @@ module RSpec
).with_description("a kind of Integer")
end

specify do
expect(
a_value_between(1, 10)
).to be_aliased_to(
be_between(1, 10)
).with_description("a value between 1 and 10 (inclusive)")
end

specify do
expect(
a_value_within(0.1).of(3)
Expand Down
74 changes: 74 additions & 0 deletions spec/rspec/matchers/built_in/be_between_spec.rb
@@ -0,0 +1,74 @@
require 'spec_helper'

describe "expect(...).to be_between(min, max)" do
it_behaves_like "an RSpec matcher", :valid_value => (10), :invalid_value => (11) do
let(:matcher) { be_between(1, 10) }
end

it "passes if target is between min and max" do
expect(10).to be_between(1, 10)
end

it "fails if target is not between min and max" do
expect {
# It does not go to 11
expect(11).to be_between(1, 10)
}.to fail_with("expected 11 to be between 1 and 10 (inclusive)")
end

it 'works with strings' do
expect("baz").to be_between("bar", "foo")
expect {
expect("foo").to be_between("bar", "baz")
}.to fail_with("expected \"foo\" to be between \"bar\" and \"baz\" (inclusive)")
end

it 'works with other Comparable objects' do
class SizeMatters
include Comparable
attr :str
def <=>(other)
str.size <=> other.str.size
end
def initialize(str)
@str = str
end
def inspect
@str
end
end
expect(SizeMatters.new("--")).to be_between(SizeMatters.new("-"), SizeMatters.new("---"))
expect {
expect(SizeMatters.new("---")).to be_between(SizeMatters.new("-"), SizeMatters.new("--"))
}.to fail_with("expected --- to be between - and -- (inclusive)")
end
end

describe "expect(...).not_to be_between(min, max)" do
it "passes if target is not between min and max" do
expect(11).not_to be_between(1, 10)
end

it "fails if target is between min and max" do
expect {
expect(10).not_to be_between(1, 10)
}.to fail_with("expected 10 not to be between 1 and 10 (inclusive)")
end
end

describe "composing with other matchers" do
it "passes when the matchers both match" do
expect([0.1, 2]).to include(a_value_between(2, 4), an_instance_of(Float))
end

it "provides a description" do
description = include(a_value_between(2, 4), an_instance_of(Float)).description
expect(description).to eq("include (a value between 2 and 4 (inclusive)) and (an instance of Float)")
end

it "fails with a clear error message when the matchers do not match" do
expect {
expect([0.1, 1]).to include(a_value_between(2, 4), an_instance_of(Float))
}.to fail_with("expected [0.1, 1] to include (a value between 2 and 4 (inclusive)) and (an instance of Float)")
end
end
15 changes: 13 additions & 2 deletions spec/rspec/matchers/description_generation_spec.rb
Expand Up @@ -45,9 +45,20 @@
expect(RSpec::Matchers.generated_description).to eq "should be > 3"
end

it "expect(...).to be between min and max" do
expect(10).to be_between(0, 10)
expect(RSpec::Matchers.generated_description).to eq "should be between 0 and 10 (inclusive)"
end

it "expect(...).to be predicate arg1, arg2 and arg3" do
expect(5.0).to be_between(0,10)
expect(RSpec::Matchers.generated_description).to eq "should be between 0 and 10"
class Parent; end
class Child < Parent
def child_of?(*parents)
parents.all? { |parent| self.is_a?(parent) }
end
end
expect(Child.new).to be_a_child_of(Parent, Object)
expect(RSpec::Matchers.generated_description).to eq "should be a child of Parent and Object"
end

it "expect(...).to equal" do
Expand Down

0 comments on commit 4f44ec9

Please sign in to comment.