Permalink
Browse files

Fix exist matcher so that it uses either #exist? or #exists?

  • Loading branch information...
1 parent 27ca1f4 commit 12d1ae30b4cdef83f13c6c2b12dd55a13cf4ef6c @myronmarston myronmarston committed with dchelimsky Jan 7, 2011
Showing with 112 additions and 58 deletions.
  1. +9 −4 features/built_in_matchers/exist.feature
  2. +13 −3 lib/rspec/matchers/exist.rb
  3. +90 −51 spec/rspec/matchers/exist_spec.rb
@@ -1,11 +1,11 @@
Feature: exist matcher
The exist matcher is used to specify that something exists
- (as indicated by #exist?):
+ (as indicated by #exist? or #exists?):
- obj.should exist # passes if obj.exist?
+ obj.should exist # passes if obj.exist? or obj.exists?
- Scenario: basic usage
+ Scenario Outline: basic usage
Given a file named "exist_matcher_spec.rb" with:
"""
class Planet
@@ -19,7 +19,7 @@ Feature: exist matcher
"<Planet: #{name}>"
end
- def exist?
+ def <predicate_method>
%w[Mercury Venus Earth Mars Jupiter Saturn Uranus Neptune].include?(name)
end
end
@@ -42,3 +42,8 @@ Feature: exist matcher
| expected <Planet: Earth> not to exist |
| expected <Planet: Tatooine> to exist |
+ Examples:
+ | predicate_method |
+ | exist? |
+ | exists? |
+
@@ -4,11 +4,21 @@ module Matchers
# should exist
# should_not exist
#
- # Passes if actual.exist?
- def exist(arg=nil)
+ # Passes if actual.exist? or actual.exists?
+ def exist(*args)
Matcher.new :exist do
match do |actual|
- arg ? actual.exist?(arg) : actual.exist?
+ predicates = [:exist?, :exists?].select { |p| actual.respond_to?(p) }
+ existance_values = predicates.map { |p| actual.send(p, *args) }
+ uniq_truthy_values = existance_values.map { |v| !!v }.uniq
+
+ case uniq_truthy_values.size
+ when 0; raise NoMethodError.new("#{actual.inspect} does not respond to either #exist? or #exists?")
+ when 1; existance_values.first
+ else raise "#exist? and #exists? returned different values:\n\n" +
+ " exist?: #{existance_values.first}\n" +
+ "exists?: #{existance_values.last}"
+ end
end
end
end
@@ -1,65 +1,104 @@
require 'spec_helper'
-class Substance
- def initialize exists, description
- @exists = exists
- @description = description
- end
- def exist?(arg=nil)
- @exists
- end
- def inspect
- @description
- end
-end
-
-class SubstanceTester
- include RSpec::Matchers
- def initialize substance
- @substance = substance
- end
- def should_exist
- @substance.should exist
- end
-end
+describe "exist matcher" do
+ context "when the object does not respond to #exist? or #exists?" do
+ subject { mock }
-describe "should exist" do
-
- before(:each) do
- @real = Substance.new true, 'something real'
- @imaginary = Substance.new false, 'something imaginary'
+ [:should, :should_not].each do |should_method|
+ describe "#{should_method} exist" do
+ it "raises an error" do
+ expect {
+ subject.send(should_method, exist)
+ }.to raise_error(NoMethodError)
+ end
+ end
+ end
end
- describe "within an example group" do
-
- it "passes if target exists" do
- @real.should exist
- end
-
- it "passes if target exists with args" do
- @real.should exist('this arg')
- end
-
- it "fails if target does not exist" do
- lambda { @imaginary.should exist }.should fail
- end
-
- it "describes itself" do
- exist.description.should == "exist"
- end
-
- it "passes should_not exist if target doesn't exist" do
- lambda { @real.should_not exist }.should fail
+ [:exist?, :exists?].each do |predicate|
+ context "when the object responds to ##{predicate}" do
+ describe "should exist" do
+ it "passes if #{predicate}" do
+ mock(predicate => true).should exist
+ end
+
+ it "fails if not #{predicate}" do
+ expect {
+ mock(predicate => false).should exist
+ }.to fail_with(/expected .* to exist/)
+ end
+ end
+
+ describe "should not exist" do
+ it "passes if not #{predicate}" do
+ mock(predicate => false).should_not exist
+ end
+
+ it "fails if #{predicate}" do
+ expect {
+ mock(predicate => true).should_not exist
+ }.to fail_with(/expected .* not to exist/)
+ end
+ end
end
end
- describe "outside of an example group" do
+ context "when the object responds to #exist? and #exists?" do
+ context "when they both return falsey values" do
+ subject { mock(:exist? => false, :exists? => nil) }
+
+ describe "should_not exist" do
+ it "passes" do
+ subject.should_not exist
+ end
+ end
+
+ describe "should exist" do
+ it "fails" do
+ expect {
+ subject.should exist
+ }.to fail_with(/expected .* to exist/)
+ end
+ end
+ end
+
+ context "when they both return truthy values" do
+ subject { mock(:exist? => true, :exists? => "something true") }
+
+ describe "should_not exist" do
+ it "fails" do
+ expect {
+ subject.should_not exist
+ }.to fail_with(/expected .* not to exist/)
+ end
+ end
- it "passes if target exists" do
- real_tester = SubstanceTester.new @real
- real_tester.should_exist
+ describe "should exist" do
+ it "passes" do
+ subject.should exist
+ end
+ end
end
+ context "when they return values with different truthiness" do
+ subject { mock(:exist? => true, :exists? => false) }
+
+ [:should, :should_not].each do |should_method|
+ describe "#{should_method} exist" do
+ it "raises an error" do
+ expect {
+ subject.send(should_method, exist)
+ }.to raise_error(/#exist\? and #exists\? returned different values/)
+ end
+ end
+ end
+ end
end
+ it 'passes any provided arguments to the call to #exist?' do
+ object = mock
+ object.should_receive(:exist?).with(:foo, :bar) { true }
+
+ object.should exist(:foo, :bar)
+ end
end

0 comments on commit 12d1ae3

Please sign in to comment.