Git Product home page Git Product logo

rspec-expectations's Introduction

RSpec Expectations Build Status Code Climate

RSpec::Expectations lets you express expected outcomes on an object in an example.

expect(account.balance).to eq(Money.new(37.42, :USD))

Install

If you want to use rspec-expectations with rspec, just install the rspec gem and RubyGems will also install rspec-expectations for you (along with rspec-core and rspec-mocks):

gem install rspec

Want to run against the main branch? You'll need to include the dependent RSpec repos as well. Add the following to your Gemfile:

%w[rspec-core rspec-expectations rspec-mocks rspec-support].each do |lib|
  gem lib, :git => "https://github.com/rspec/#{lib}.git", :branch => 'main'
end

If you want to use rspec-expectations with another tool, like Test::Unit, Minitest, or Cucumber, you can install it directly:

gem install rspec-expectations

Contributing

Once you've set up the environment, you'll need to cd into the working directory of whichever repo you want to work in. From there you can run the specs and cucumber features, and make patches.

NOTE: You do not need to use rspec-dev to work on a specific RSpec repo. You can treat each RSpec repo as an independent project.

Basic usage

Here's an example using rspec-core:

RSpec.describe Order do
  it "sums the prices of the items in its line items" do
    order = Order.new
    order.add_entry(LineItem.new(:item => Item.new(
      :price => Money.new(1.11, :USD)
    )))
    order.add_entry(LineItem.new(:item => Item.new(
      :price => Money.new(2.22, :USD),
      :quantity => 2
    )))
    expect(order.total).to eq(Money.new(5.55, :USD))
  end
end

The describe and it methods come from rspec-core. The Order, LineItem, Item and Money classes would be from your code. The last line of the example expresses an expected outcome. If order.total == Money.new(5.55, :USD), then the example passes. If not, it fails with a message like:

  expected: #<Money @value=5.55 @currency=:USD>
       got: #<Money @value=1.11 @currency=:USD>

Built-in matchers

Equivalence

expect(actual).to eq(expected)  # passes if actual == expected
expect(actual).to eql(expected) # passes if actual.eql?(expected)
expect(actual).not_to eql(not_expected) # passes if not(actual.eql?(expected))

Note: The new expect syntax no longer supports the == matcher.

Identity

expect(actual).to be(expected)    # passes if actual.equal?(expected)
expect(actual).to equal(expected) # passes if actual.equal?(expected)

Comparisons

expect(actual).to be >  expected
expect(actual).to be >= expected
expect(actual).to be <= expected
expect(actual).to be <  expected
expect(actual).to be_within(delta).of(expected)

Regular expressions

expect(actual).to match(/expression/)

Note: The new expect syntax no longer supports the =~ matcher.

Types/classes

expect(actual).to be_an_instance_of(expected) # passes if actual.class == expected
expect(actual).to be_a(expected)              # passes if actual.kind_of?(expected)
expect(actual).to be_an(expected)             # an alias for be_a
expect(actual).to be_a_kind_of(expected)      # another alias

Truthiness

expect(actual).to be_truthy   # passes if actual is truthy (not nil or false)
expect(actual).to be true     # passes if actual == true
expect(actual).to be_falsy    # passes if actual is falsy (nil or false)
expect(actual).to be false    # passes if actual == false
expect(actual).to be_nil      # passes if actual is nil
expect(actual).to_not be_nil  # passes if actual is not nil

Expecting errors

expect { ... }.to raise_error
expect { ... }.to raise_error(ErrorClass)
expect { ... }.to raise_error("message")
expect { ... }.to raise_error(ErrorClass, "message")

Expecting throws

expect { ... }.to throw_symbol
expect { ... }.to throw_symbol(:symbol)
expect { ... }.to throw_symbol(:symbol, 'value')

Yielding

expect { |b| 5.tap(&b) }.to yield_control # passes regardless of yielded args

expect { |b| yield_if_true(true, &b) }.to yield_with_no_args # passes only if no args are yielded

expect { |b| 5.tap(&b) }.to yield_with_args(5)
expect { |b| 5.tap(&b) }.to yield_with_args(Integer)
expect { |b| "a string".tap(&b) }.to yield_with_args(/str/)

expect { |b| [1, 2, 3].each(&b) }.to yield_successive_args(1, 2, 3)
expect { |b| { :a => 1, :b => 2 }.each(&b) }.to yield_successive_args([:a, 1], [:b, 2])

Predicate matchers

expect(actual).to be_xxx         # passes if actual.xxx?
expect(actual).to have_xxx(:arg) # passes if actual.has_xxx?(:arg)

Ranges (Ruby >= 1.9 only)

expect(1..10).to cover(3)

Collection membership

# exact order, entire collection
expect(actual).to eq(expected)

# exact order, partial collection (based on an exact position)
expect(actual).to start_with(expected)
expect(actual).to end_with(expected)

# any order, entire collection
expect(actual).to match_array(expected)

# You can also express this by passing the expected elements
# as individual arguments
expect(actual).to contain_exactly(expected_element1, expected_element2)

 # any order, partial collection
expect(actual).to include(expected)

Examples

expect([1, 2, 3]).to eq([1, 2, 3])            # Order dependent equality check
expect([1, 2, 3]).to include(1)               # Exact ordering, partial collection matches
expect([1, 2, 3]).to include(2, 3)            #
expect([1, 2, 3]).to start_with(1)            # As above, but from the start of the collection
expect([1, 2, 3]).to start_with(1, 2)         #
expect([1, 2, 3]).to end_with(3)              # As above but from the end of the collection
expect([1, 2, 3]).to end_with(2, 3)           #
expect({:a => 'b'}).to include(:a => 'b')     # Matching within hashes
expect("this string").to include("is str")    # Matching within strings
expect("this string").to start_with("this")   #
expect("this string").to end_with("ring")     #
expect([1, 2, 3]).to contain_exactly(2, 3, 1) # Order independent matches
expect([1, 2, 3]).to match_array([3, 2, 1])   #

# Order dependent compound matchers
expect(
  [{:a => 'hash'},{:a => 'another'}]
).to match([a_hash_including(:a => 'hash'), a_hash_including(:a => 'another')])

should syntax

In addition to the expect syntax, rspec-expectations continues to support the should syntax:

actual.should eq expected
actual.should be > 3
[1, 2, 3].should_not include 4

See detailed information on the should syntax and its usage.

Compound Matcher Expressions

You can also create compound matcher expressions using and or or:

expect(alphabet).to start_with("a").and end_with("z")
expect(stoplight.color).to eq("red").or eq("green").or eq("yellow")

Composing Matchers

Many of the built-in matchers are designed to take matchers as arguments, to allow you to flexibly specify only the essential aspects of an object or data structure. In addition, all of the built-in matchers have one or more aliases that provide better phrasing for when they are used as arguments to another matcher.

Examples

expect { k += 1.05 }.to change { k }.by( a_value_within(0.1).of(1.0) )

expect { s = "barn" }.to change { s }
  .from( a_string_matching(/foo/) )
  .to( a_string_matching(/bar/) )

expect(["barn", 2.45]).to contain_exactly(
  a_value_within(0.1).of(2.5),
  a_string_starting_with("bar")
)

expect(["barn", "food", 2.45]).to end_with(
  a_string_matching("foo"),
  a_value > 2
)

expect(["barn", 2.45]).to include( a_string_starting_with("bar") )

expect(:a => "food", :b => "good").to include(:a => a_string_matching(/foo/))

hash = {
  :a => {
    :b => ["foo", 5],
    :c => { :d => 2.05 }
  }
}

expect(hash).to match(
  :a => {
    :b => a_collection_containing_exactly(
      a_string_starting_with("f"),
      an_instance_of(Integer)
    ),
    :c => { :d => (a_value < 3) }
  }
)

expect { |probe|
  [1, 2, 3].each(&probe)
}.to yield_successive_args( a_value < 2, 2, a_value > 2 )

Usage outside rspec-core

You always need to load rspec/expectations even if you only want to use one part of the library:

require 'rspec/expectations'

Then simply include RSpec::Matchers in any class:

class MyClass
  include RSpec::Matchers

  def do_something(arg)
    expect(arg).to be > 0
    # do other stuff
  end
end

Also see

rspec-expectations's People

Contributors

alexcoplan avatar alindeman avatar benoittgt avatar chrisarcand avatar cupakromer avatar danielfone avatar dchelimsky avatar eloyesp avatar gautamkpai avatar jeremywadsack avatar jonrowe avatar justinko avatar lucapette avatar lukeredpath avatar marcandre avatar myronmarston avatar nevinera avatar phiggins avatar pirj avatar porras avatar priteshjain avatar r00k avatar reitermarkus avatar sferik avatar sleepingkingstudios avatar soulcutter avatar spicycode avatar xaviershay avatar yujinakayama avatar zsyed91 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

rspec-expectations's Issues

Add a matcher for specifying that a method yields

Without a matcher for specifying what is yielded we can do this:

class Foo
  def method_that_yields
    yield
  end

  def method_that_does_not_yield
  end
end
describe Foo do
  describe "method_that_yields" do
    it "yields" do
      foo = Foo.new
      yielded = []
      foo.method_that_yields do
        yielded << true
      end
      yielded.should == [true]
    end
  end
  describe "method_that_does_not_yield" do
    it "does not yield" do
      foo = Foo.new
      yielded = []
      foo.method_that_does_not_yield do
        yielded << true
      end
      yielded.should == []
    end
  end
end

But a first-class matcher would be nicer

rdoc.info failure - .document file

Noticed that your documentation failed to build on rdoc.info. Looks like the reason is that your .document file incorrectly lists a README.rdoc file for inclusion, and that file doesn't actually exist. I'd suggest changing the line in .document to README.markdown and give it another shot at generating. Should fix it. If not, let me know and I'll be happy to help.

stack level too deep with engines

I'm trying to build an engine http://github.com/radar/forem/tree/broken using RSpec and it gives a "Stack level too deep" on an undefined method when I run bundle exec rspec spec/integration. This is because the forum_path method is undefined, but it also happens with any other undefined method.

The problem is that you get a stack trace like this:

.../gems/rspec-expectations-2.5.0/lib/rspec/matchers/method_missing.rb:9:in `method_missing'
.../gems/rails-5f1fc0c8ac6e/actionpack/lib/action_dispatch/testing/assertions/routing.rb:175:in `method_missing'
.../gems/rails-5f1fc0c8ac6e/actionpack/lib/action_dispatch/testing/integration.rb:378:in `method_missing'
.../gems/rspec-expectations-2.5.0/lib/rspec/matchers/method_missing.rb:9:in `method_missing'
..........

As you can see here, it's in an infinite loop, causing the stack level too deep error. I'm interested in any insight that can be offered on this.

Change matcher keeps object reference

I'm trying to do this:

require 'rubygems'
require 'rspec'

class TestBug
  attr_reader :array

  def initialize
    @array = []
  end

  def add(n)
    @array << n
  end
end

describe TestBug do
  it "should NOT fail" do
    @bug = TestBug.new

    expect {
      @bug.add(1)
    }.to change {
      @bug.array
    }.from([]).to([1])
  end
end

I think this spec should not fail. I debbuged the matchers/change.rb file and noticed that after the line 17 gets executed, the value of @before changes to the same value as @after

  # lib/rspec/matchers/change.rb
  def matches?(event_proc)
    raise_block_syntax_error if block_given?

    @before = evaluate_value_proc
    event_proc.call # <- HERE
    @after = evaluate_value_proc

    (!change_expected? || changed?) && matches_before? && matches_after? && matches_amount? && matches_min? && matches_max?
  end

Is this a bug or I'm trying to do something wrong?

Undefined variable in Cucumber step definition triggers SystemStackError in rspec

In a Rails 3.0.5 application with Ruby 1.9.2p180, rspec-expectations 2.5.0 and Cucumber 0.10.2, I am experiencing that SystemStackError is raised from within RSpec. The trigger is to refer to an undefined variable within a Cucumber step definition, like so:

Given /^I am logged in as "([^"]+)"$/ do |username|
  define_me
end

This results in SystemStackError from line 7 of /usr/local/rvm/gems/ruby-1.9.2-p180/gems/rspec-expectations-2.5.0/lib/rspec/matchers/method_missing.rb.

See my IRC conversation with Aslak Hellesรธy for reference.

Using polymorphic_path throws a strange error also, not available

In a controller test we had an assertion:

response.should redirect_to(polymorphic_path(@some_object))

Which resulted in the following error

Failure/Error: Unable to find matching line from backtrace stack level too deep
# ~/.rvm/gems/ruby-1.9.2-p0@gemset/gems/rspec-expectations-2.1.0/lib/rspec/matchers/method_missing.rb:4

Seems like there might be two issues here:

  1. If some_route_path method is available why isn't polymorphic path available as well? (maybe this is not the job of the expectations code)
  2. Why is it stack overflowing in method_missing? If I remove the assertion and just reduce the line to "polymorphic_path(@some_object)" I get the same thing.

This is on Rails 3.0.3 and rspec 2.1.0 and Ruby 1.9.2-p0

Let me know if you need any other details.

obj.should != something doesn't work properly

A coworker of mine who has never used RSpec but is interested in it pointed me to this code snippet from zenspider:

describe Integer do
  it "sucks" do
    10.should == 10
    10.should != 10
  end
end

This spec passes. I know that the recommended RSpec way is to use should_not == rather than should != but this is definitely very confusing behavior.

I was going to go fix it, but this is such a simple case that I'm surprised it hasn't been addressed before, and it made me wonder if there's a specific reason it's not supported. If we're only going to support should_not == then we should at least give the user an error or warning when they use should != as this is very confusing behavior.

result should have been changed by 0, but was changed by 0

Just jumped from beta.20 to beta.22 and I'm seeing this new failure:

 Failure/Error: expect do
 result should have been changed by 0, but was changed by 0

This is a spec to ensure that, given a model with tag "foo", tagging it again with "foo" doesn't actually increase the tag count.

The relevant part of the spec reads like this:

    expect do
      model.tag 'foo'
    end.to change { model.tags.size }.by(0)

There are other specs in the same file which all work, but they expect to change by(1) or some other non-zero value. Seems that something about by(0) is busted.

Workaround for now: rewrite by(0) specs like this:

    expect do
      model.tag 'foo'
    end.to_not change { model.tags.size }

Perhaps I should have written it this way in the first place, but the change by(0) version followed immediately after a run of other specs which all did change by(x), so I just followed the same pattern.

Cheers,
Wincent

expectation not met : "should include" fails if target lacks both of the keys

I ran all of the specs and all of them pass, except for this one:

  1. should include(:key1 => value1, :key2 => value2) for a hash target fails if target lacks both of the keys
    Failure/Error: lambda {
    expected RSpec::Expectations::ExpectationNotMetError with "expected {:a=>1, :b=>1} to include {:c=>1, :d=>1}", got #<RSpec::Expectations::ExpectationNotMetError: expected {:a=>1, :b=>1} to include {:d=>1, :c=>1}>

    ./spec/rspec/matchers/include_spec.rb:275

Is this an error?

Reporting error

it "test test" do
    
    @mymock = mock(:test)
    @mymock.should_receive(:test).with(1,2,3).once.ordered
    @mymock.should_receive(:test).with(3,2,1).once.ordered

    @mymock.test(1,2,3)
    @mymock.test(3,2,2)
    
  end

outputs:

Failures:
  1) TestTest test test
     Failure/Error: @mymock.test(3,2,2)
     Mock :test received :test with unexpected arguments
       expected: (1, 2, 3)
            got: (3, 2, 2)

Shouldn't it report:

expected: (3,2,1)
        got: (3,2,2)

Should Matchers be automatically included in ActiveSupport TestCases?

Hi,

Trying to use test/unit with only rspec-expectations has lead me to a question: if just requiring "rspec/expectations" in your test_helper.rb properly extends Kernel with "should" and "should_not" methods (enabling you to do number.should == 3), Matchers module only includes its methods in RSpec.

In order to use the Matchers now you have to include RSpec::Matchers inside ActiveSupport::TestCase class, in test_helper.rb. But this isn't obvious, as expectations work out-of-the-box (as they extend Kernel once required). Why not include Matchers in ActiveSupport::TestCase as you require 'rspec/expectations'? It could be something like:

module Matchers
    if RSpec.respond_to?(:configure)
      RSpec.configure {|c| c.include self}  # This is what it does now
    end
    if defined?(ActiveSupport::TestCase)
      ActiveSupport::TestCase.send(:include, self)
    end
end

Would that be too obtrusive for an app using both test/unit and rspec? Or is it reasonable to assume that when you require rspec/expectations you definitely want the matchers too?

"a string".should_not respond_to(:reverse, :some_other_method) does not fail

I just noticed that this doesn't fail:

"a string".should_not respond_to(:reverse, :some_other_method)

It passes because "a string" does not respond_to :some_other_method. I guess this is an issue of semantics--does the matcher mean it does not respond to any of the given methods, or to all of the given methods?

I personally think the current behavior is confusing. As I see it, this multi-method form is basically a replacement for:

"a string".should_not respond_to(:reverse)
"a string".should_not respond_to(:some_other_method)

...except that this would fail, while the multi-method form passes, as it currently works.

I'm happy to fix this, but wanted to ask about the intended behavior first.

=~ operator requires items to implement comparable

Continuation of rspec/rspec-core#413 (comment).

Using latest rspec (2.6.x): when you need to compare arrays ignoring the ordering of the elements you have to use the ~= instead of the == operator. Internally ~= seems to compare the objects using <=> (I guess) and so you get an

ArgumentError:
   comparison of Dossier with Dossier failed

if the elements don't implement comparable. This should be changed so that ~= works on all arrays just like == does.

The ArgumentError is only thrown when the test fails. If the test passes everything works as expected.

Dossier is a bare rails model (instance of ActiveRecord::Base).

Have matcher fails when ActiveSupport partially loaded

The Have matcher checks for the definition of ActiveSupport::Inflector when evaluating the matching, and assumes that if ActiveSupport::Inflector is defined that it responds to pluralize. ActiveSupport 3.0 can be partially loaded so that ActiveSupport::Inflector is defined but does not respond to pluralize because inflector/methods.rb has been loaded but inflector/inflections.rb has not. I've done this in a gem I'm developing by cherry picking active_support/dependencies/autoload which requires active_support/inflector/methods but not active_support/inflector/inflections

Mark spec as pending when no expectations have been made

Currently, RSpec will consider a spec like this as green.

it "does nothing" do
end

Yeah, this example is not a great one, but here's a real test case. Imagine that I have something like this.

it "yields the configuration" do
  RSpec.configure do |config|
    config.should be_an_instance_of(RSpec::Core::Configuration)
  end
end

If I haven't correctly implemented the RSpec.configure method and I'm not yielding anything, RSpec will still consider it as green.

One solution would be rewrite the spec above as

it "yields the configuration" do
  subject = nil

  RSpec.configure do |config|
    subject = config
  end

  subject.should be_an_instance_of(RSpec::Core::Configuration)
end

but that looks pretty ugly to me.

I think that would be pretty valuable to mark such spec as pending, explaining that the spec was implemented, but no expectations have been made.

Ruby 1.9.2 defines <=> for all objects, breaking =~ for many.

ruby core issue 24063 added Kernel::<=> to Object (note that Kernel module documentation does not display this functionality).

as a result, safe_sort isn't anymore, and trying to =~ any objects that don't define a better <=> throws an exception.

i'll attach a somewhat plausible patch momentarily

lib/rspec/matchers.rb contains non-us-ascii characters

On the line with '# Include Matchers', after the '#'.

rcov 0.9.9 chokes on this on Ruby 1.9. (Invalid byte sequence in US-ASCII).

A simple 'grep' for the encoding magic + 'file' may detect the same problem other ruby files.

Thanks!

Idea: `each_satisfy` matcher

Recently ran across some coworker's code who had written:

results.each { |r| r.cost.should be <= 5 }

While this is pretty readable, I wondered if it would make sense to write an rspec matcher that used #each (Enumerable) so the syntax could be even clearer:

results.should each_satisfy { |r| r.cost <= 5 }

The second syntax would also be easier to make diffable.

Let me know thoughts. If agreed, I can make it happen.

RSpec::Matchers::Change with from(true).to(false) change

require 'spec_helper'

describe RSpec::Matchers::Change do
  it "sets wrong failure message" do
    @a = true
    lambda {
      nil
    }.should change { @a }.from(true).to(false)
  end

  it "passes successfully wrong change" do
    @a = true
    lambda {
      @a = 1
    }.should change { @a }.from(true).to(false)
  end
end

prints

RSpec::Matchers::Change
  sets wrong failure message (FAILED - 1)
  passes successfully wrong change

Failures:
  1) RSpec::Matchers::Change sets wrong failure message
     Failure/Error: lambda {
     result should have initially been true, but was true
     # /usr/local/rvm/gems/ree-1.8.7-2010.02@rails3/gems/rspec-expectations-2.0.0.beta.20/lib/rspec/expectations/fail_with.rb:29:in `fail_with'
     # /usr/local/rvm/gems/ree-1.8.7-2010.02@rails3/gems/rspec-expectations-2.0.0.beta.20/lib/rspec/expectations/handler.rb:21:in `handle_matcher'
     # /usr/local/rvm/gems/ree-1.8.7-2010.02@rails3/gems/rspec-expectations-2.0.0.beta.20/lib/rspec/expectations/extensions/kernel.rb:27:in `should'
     # ./spec/models/test_spec.rb:6

UTF-8 character in spec causes an error when the expectation fails

I am not sure if I should be reporting this here or in core...

I am using 1.9.2 and have a spec which has a an expectation with a utf8 character. When I run the specs I get this error:

http://gist.github.com/527804

If I make the change in the application code to handle the encoding then the spec passes.

I was expecting a Ruby 1.9 encoding error as the application code does a join with utf-8 and binary strings which 1.9 doesn't like:

incompatible character encodings: UTF-8 and ASCII-8BIT (Encoding::CompatibilityError)

comparing arrays of multiple hashes with different keys and/or values using the =~ operator results in ambiguous error

Seriously, it's that specific of an error. Bear with me while I copy/paste this really big test suite.

# spec/hash_comparison_spec.rb
require 'spec_helper'

describe "hash comparison tests" do
  context "comparing single hashes with same keys, different values" do
    let(:me)  { { :foo => 'bar' } }
    let(:me2) { { :foo => 'baz' } }

    it "should fail gracefully when using ==" do
      me.should == me2
    end

    it "should fail gracefully when using eq()" do
      me.should eq(me2)
    end

    it "should fail gracefully when using =~" do
      me.should =~ me2
    end
  end

  context "comparing single hashes with different keys, different values" do
    let(:me)  { { :foo => 'bar' } }
    let(:me2) { { :bar => 'baz' } }

    it "should fail gracefully when using ==" do
      me.should == me2
    end

    it "should fail gracefully when using eq()" do
      me.should eq(me2)
    end

    it "should fail gracefully when using =~" do
      me.should =~ me2
    end
  end

  context "comparing an array of single hashes with same keys, different values" do
    let(:me)  { [{ :foo => 'bar' }] }
    let(:me2) { [{ :foo => 'baz' }] }

    it "should fail gracefully when using ==" do
      me.should == me2
    end

    it "should fail gracefully when using eq()" do
      me.should eq(me2)
    end

    it "should fail gracefully when using =~" do
      me.should =~ me2
    end
  end

  context "comparing an array of single hashes with different keys, different values" do
    let(:me)  { [{ :foo => 'bar' }] }
    let(:me2) { [{ :bar => 'baz' }] }

    it "should fail gracefully when using ==" do
      me.should == me2
    end

    it "should fail gracefully when using eq()" do
      me.should eq(me2)
    end

    it "should fail gracefully when using =~" do
      me.should =~ me2
    end
  end

  context "comparing an array of multiple hashes with same keys, same values" do
    let(:me3) { [{ :foo => 'bar' }, { :biz => 'bang' }] }
    let(:me4) { [{ :foo => 'bar' }, { :biz => 'bang' }] }

    it "should pass when using ==" do
      me3.should == me4
    end

    it "should pass when using eq()" do
      me3.should eq(me4)
    end

    it "should pass when using =~" do
      me3.should =~ me4
    end
  end

  context "comparing an array of multiple hashes with same keys, different values" do
    let(:me3) { [{ :foo => 'bar' }, { :biz => 'bang' }] }
    let(:me4) { [{ :foo => 'bang' }, { :biz => 'bar' }] }

    it "should fail gracefully when using ==" do
      me3.should == me4
    end

    it "should fail gracefully when using eq()" do
      me3.should eq(me4)
    end

    it "should fail and show me an ambiguous error when using =~" do
      me3.should =~ me4
    end
  end

  context "comparing an array of hashes with different keys, different values" do
    let(:me3) { [{ :foo => 'bar' }, { :biz => 'bang' }] }
    let(:me4) { [{ :bar => 'foo' }, { :bang => 'biz' }] }

    it "should fail gracefully when using ==" do
      me3.should == me4
    end

    it "should fail gracefully when using eq()" do
      me3.should eq(me4)
    end

    it "should fail and show me an ambiguous error when using =~" do
      me3.should =~ me4
    end
  end
end

And the Really Big Output

$ rspec spec/hash_comparison_spec.rb --format documentation

hash comparison tests
  comparing single hashes with same keys, different values
    should fail gracefully when using == (FAILED - 1)
    should fail gracefully when using eq() (FAILED - 2)
    should fail gracefully when using =~ (FAILED - 3)
  comparing single hashes with different keys, different values
    should fail gracefully when using == (FAILED - 4)
    should fail gracefully when using eq() (FAILED - 5)
    should fail gracefully when using =~ (FAILED - 6)
  comparing an array of single hashes with same keys, different values
    should fail gracefully when using == (FAILED - 7)
    should fail gracefully when using eq() (FAILED - 8)
    should fail gracefully when using =~ (FAILED - 9)
  comparing an array of single hashes with different keys, different values
    should fail gracefully when using == (FAILED - 10)
    should fail gracefully when using eq() (FAILED - 11)
    should fail gracefully when using =~ (FAILED - 12)
  comparing an array of multiple hashes with same keys, same values
    should pass when using ==
    should pass when using eq()
    should pass when using =~
  comparing an array of multiple hashes with same keys, different values
    should fail gracefully when using == (FAILED - 13)
    should fail gracefully when using eq() (FAILED - 14)
    should fail and show me an ambiguous error when using =~ (FAILED - 15)
  comparing an array of hashes with different keys, different values
    should fail gracefully when using == (FAILED - 16)
    should fail gracefully when using eq() (FAILED - 17)
    should fail and show me an ambiguous error when using =~ (FAILED - 18)

Failures:

  1) hash comparison tests comparing single hashes with same keys, different values should fail gracefully when using ==
     Failure/Error: me.should == me2
       expected: {:foo=>"baz"}
            got: {:foo=>"bar"} (using ==)
       Diff:
       @@ -1,2 +1,2 @@
       -{:foo=>"baz"}
       +{:foo=>"bar"}
     # ./spec/hash_comparison_spec.rb:9:in `block (3 levels) in <top (required)>'

  2) hash comparison tests comparing single hashes with same keys, different values should fail gracefully when using eq()
     Failure/Error: me.should eq(me2)

       expected {:foo=>"baz"}
            got {:foo=>"bar"}

       (compared using ==)

       Diff:
       @@ -1,2 +1,2 @@
       -{:foo=>"baz"}
       +{:foo=>"bar"}
     # ./spec/hash_comparison_spec.rb:13:in `block (3 levels) in <top (required)>'

  3) hash comparison tests comparing single hashes with same keys, different values should fail gracefully when using =~
     Failure/Error: me.should =~ me2
       expected: {:foo=>"baz"}
            got: {:foo=>"bar"} (using =~)
       Diff:
       @@ -1,2 +1,2 @@
       -{:foo=>"baz"}
       +{:foo=>"bar"}
     # ./spec/hash_comparison_spec.rb:17:in `block (3 levels) in <top (required)>'

  4) hash comparison tests comparing single hashes with different keys, different values should fail gracefully when using ==
     Failure/Error: me.should == me2
       expected: {:bar=>"baz"}
            got: {:foo=>"bar"} (using ==)
       Diff:
       @@ -1,2 +1,2 @@
       -{:bar=>"baz"}
       +{:foo=>"bar"}
     # ./spec/hash_comparison_spec.rb:26:in `block (3 levels) in <top (required)>'

  5) hash comparison tests comparing single hashes with different keys, different values should fail gracefully when using eq()
     Failure/Error: me.should eq(me2)

       expected {:bar=>"baz"}
            got {:foo=>"bar"}

       (compared using ==)

       Diff:
       @@ -1,2 +1,2 @@
       -{:bar=>"baz"}
       +{:foo=>"bar"}
     # ./spec/hash_comparison_spec.rb:30:in `block (3 levels) in <top (required)>'

  6) hash comparison tests comparing single hashes with different keys, different values should fail gracefully when using =~
     Failure/Error: me.should =~ me2
       expected: {:bar=>"baz"}
            got: {:foo=>"bar"} (using =~)
       Diff:
       @@ -1,2 +1,2 @@
       -{:bar=>"baz"}
       +{:foo=>"bar"}
     # ./spec/hash_comparison_spec.rb:34:in `block (3 levels) in <top (required)>'

  7) hash comparison tests comparing an array of single hashes with same keys, different values should fail gracefully when using ==
     Failure/Error: me.should == me2
       expected: [{:foo=>"baz"}]
            got: [{:foo=>"bar"}] (using ==)
       Diff:
       @@ -1,2 +1,2 @@
       -[{:foo=>"baz"}]
       +[{:foo=>"bar"}]
     # ./spec/hash_comparison_spec.rb:43:in `block (3 levels) in <top (required)>'

  8) hash comparison tests comparing an array of single hashes with same keys, different values should fail gracefully when using eq()
     Failure/Error: me.should eq(me2)

       expected [{:foo=>"baz"}]
            got [{:foo=>"bar"}]

       (compared using ==)

       Diff:
       @@ -1,2 +1,2 @@
       -[{:foo=>"baz"}]
       +[{:foo=>"bar"}]
     # ./spec/hash_comparison_spec.rb:47:in `block (3 levels) in <top (required)>'

  9) hash comparison tests comparing an array of single hashes with same keys, different values should fail gracefully when using =~
     Failure/Error: me.should =~ me2
       expected collection contained:  [{:foo=>"baz"}]
       actual collection contained:    [{:foo=>"bar"}]
       the missing elements were:      [{:foo=>"baz"}]
       the extra elements were:        [{:foo=>"bar"}]
     # ./spec/hash_comparison_spec.rb:51:in `block (3 levels) in <top (required)>'

  10) hash comparison tests comparing an array of single hashes with different keys, different values should fail gracefully when using ==
     Failure/Error: me.should == me2
       expected: [{:bar=>"baz"}]
            got: [{:foo=>"bar"}] (using ==)
       Diff:
       @@ -1,2 +1,2 @@
       -[{:bar=>"baz"}]
       +[{:foo=>"bar"}]
     # ./spec/hash_comparison_spec.rb:60:in `block (3 levels) in <top (required)>'

  11) hash comparison tests comparing an array of single hashes with different keys, different values should fail gracefully when using eq()
     Failure/Error: me.should eq(me2)

       expected [{:bar=>"baz"}]
            got [{:foo=>"bar"}]

       (compared using ==)

       Diff:
       @@ -1,2 +1,2 @@
       -[{:bar=>"baz"}]
       +[{:foo=>"bar"}]
     # ./spec/hash_comparison_spec.rb:64:in `block (3 levels) in <top (required)>'

  12) hash comparison tests comparing an array of single hashes with different keys, different values should fail gracefully when using =~
     Failure/Error: me.should =~ me2
       expected collection contained:  [{:bar=>"baz"}]
       actual collection contained:    [{:foo=>"bar"}]
       the missing elements were:      [{:bar=>"baz"}]
       the extra elements were:        [{:foo=>"bar"}]
     # ./spec/hash_comparison_spec.rb:68:in `block (3 levels) in <top (required)>'

  13) hash comparison tests comparing an array of multiple hashes with same keys, different values should fail gracefully when using ==
     Failure/Error: me3.should == me4
       expected: [{:foo=>"bang"}, {:biz=>"bar"}]
            got: [{:foo=>"bar"}, {:biz=>"bang"}] (using ==)
       Diff:
       @@ -1,2 +1,2 @@
       -[{:foo=>"bang"}, {:biz=>"bar"}]
       +[{:foo=>"bar"}, {:biz=>"bang"}]
     # ./spec/hash_comparison_spec.rb:94:in `block (3 levels) in <top (required)>'

  14) hash comparison tests comparing an array of multiple hashes with same keys, different values should fail gracefully when using eq()
     Failure/Error: me3.should eq(me4)

       expected [{:foo=>"bang"}, {:biz=>"bar"}]
            got [{:foo=>"bar"}, {:biz=>"bang"}]

       (compared using ==)

       Diff:
       @@ -1,2 +1,2 @@
       -[{:foo=>"bang"}, {:biz=>"bar"}]
       +[{:foo=>"bar"}, {:biz=>"bang"}]
     # ./spec/hash_comparison_spec.rb:98:in `block (3 levels) in <top (required)>'

  15) hash comparison tests comparing an array of multiple hashes with same keys, different values should fail and show me an ambiguous error when using =~
     Failure/Error: me3.should =~ me4
     ArgumentError:
       comparison of Hash with Hash failed
     # ./spec/hash_comparison_spec.rb:102:in `block (3 levels) in <top (required)>'

  16) hash comparison tests comparing an array of hashes with different keys, different values should fail gracefully when using ==
     Failure/Error: me3.should == me4
       expected: [{:bar=>"foo"}, {:bang=>"biz"}]
            got: [{:foo=>"bar"}, {:biz=>"bang"}] (using ==)
       Diff:
       @@ -1,2 +1,2 @@
       -[{:bar=>"foo"}, {:bang=>"biz"}]
       +[{:foo=>"bar"}, {:biz=>"bang"}]
     # ./spec/hash_comparison_spec.rb:111:in `block (3 levels) in <top (required)>'

  17) hash comparison tests comparing an array of hashes with different keys, different values should fail gracefully when using eq()
     Failure/Error: me3.should eq(me4)

       expected [{:bar=>"foo"}, {:bang=>"biz"}]
            got [{:foo=>"bar"}, {:biz=>"bang"}]

       (compared using ==)

       Diff:
       @@ -1,2 +1,2 @@
       -[{:bar=>"foo"}, {:bang=>"biz"}]
       +[{:foo=>"bar"}, {:biz=>"bang"}]
     # ./spec/hash_comparison_spec.rb:115:in `block (3 levels) in <top (required)>'

  18) hash comparison tests comparing an array of hashes with different keys, different values should fail and show me an ambiguous error when using =~
     Failure/Error: me3.should =~ me4
     ArgumentError:
       comparison of Hash with Hash failed
     # ./spec/hash_comparison_spec.rb:119:in `block (3 levels) in <top (required)>'

Finished in 1.35 seconds
21 examples, 18 failures

Failed examples:

rspec ./spec/hash_comparison_spec.rb:8 # hash comparison tests comparing single hashes with same keys, different values should fail gracefully when using ==
rspec ./spec/hash_comparison_spec.rb:12 # hash comparison tests comparing single hashes with same keys, different values should fail gracefully when using eq()
rspec ./spec/hash_comparison_spec.rb:16 # hash comparison tests comparing single hashes with same keys, different values should fail gracefully when using =~
rspec ./spec/hash_comparison_spec.rb:25 # hash comparison tests comparing single hashes with different keys, different values should fail gracefully when using ==
rspec ./spec/hash_comparison_spec.rb:29 # hash comparison tests comparing single hashes with different keys, different values should fail gracefully when using eq()
rspec ./spec/hash_comparison_spec.rb:33 # hash comparison tests comparing single hashes with different keys, different values should fail gracefully when using =~
rspec ./spec/hash_comparison_spec.rb:42 # hash comparison tests comparing an array of single hashes with same keys, different values should fail gracefully when using ==
rspec ./spec/hash_comparison_spec.rb:46 # hash comparison tests comparing an array of single hashes with same keys, different values should fail gracefully when using eq()
rspec ./spec/hash_comparison_spec.rb:50 # hash comparison tests comparing an array of single hashes with same keys, different values should fail gracefully when using =~
rspec ./spec/hash_comparison_spec.rb:59 # hash comparison tests comparing an array of single hashes with different keys, different values should fail gracefully when using ==
rspec ./spec/hash_comparison_spec.rb:63 # hash comparison tests comparing an array of single hashes with different keys, different values should fail gracefully when using eq()
rspec ./spec/hash_comparison_spec.rb:67 # hash comparison tests comparing an array of single hashes with different keys, different values should fail gracefully when using =~
rspec ./spec/hash_comparison_spec.rb:93 # hash comparison tests comparing an array of multiple hashes with same keys, different values should fail gracefully when using ==
rspec ./spec/hash_comparison_spec.rb:97 # hash comparison tests comparing an array of multiple hashes with same keys, different values should fail gracefully when using eq()
rspec ./spec/hash_comparison_spec.rb:101 # hash comparison tests comparing an array of multiple hashes with same keys, different values should fail and show me an ambiguous error when using =~
rspec ./spec/hash_comparison_spec.rb:110 # hash comparison tests comparing an array of hashes with different keys, different values should fail gracefully when using ==
rspec ./spec/hash_comparison_spec.rb:114 # hash comparison tests comparing an array of hashes with different keys, different values should fail gracefully when using eq()
rspec ./spec/hash_comparison_spec.rb:118 # hash comparison tests comparing an array of hashes with different keys, different values should fail and show me an ambiguous error when using =~

You will notice that all of the errors except for #15 and #18 contain diffs, while those other two report only ArgumentError: comparison of Hash with Hash failed

Let me scope matchers

RSpec::Matchers.define makes matchers available to all example groups. We need a means of scoping some matchers, so they only appear in contexts deemed relevant by their author.

One approach would be for the definition to determine whether it is in the top level Object or not. If so, define itself in RSpec::Matchers, otherwise just define it in the current scope.

change matcher fails where it used to succeed

As an educational exercise, I am attempting to upgrade the rspec version used by the mail gem (see topic branch on my fork if desired) from ~>1.3.0 to ~> 2.6.0. Once I upgraded and removed all errors and deprecation warnings, I ran the test suite. There was a single test failure under 2.6.0 and none under 1.3.0 (MRI 1.9.2 in all cases). The spec that is failing uses the should_not change matcher.

I stepped my rspec dependency back to ~> 2.0.0 and re-ran the tests. All pass. Ditto for ~> 2.1.0. When I went to ~> 2.2.0, bundler included rspec-expectations 2.6 as a dependency. This is when the test suite failed. I noticed some changes to the change matcher in the 2.6 changelog and though it might worth raising the issue.

Judging by the test output, the object does actually change, but I found it curious that previous version of RSpec allowed this test to pass. Is the current implementation broken or was the older implementation wrong?

See Also: Gemfile.lock

Diffable include matcher

Currently the include matcher doesn't diff strings on failure like other matchers used to compare strings. This behavior was surprising to me. Is there a reason not to change it? I'd be willing to help get this done.

Matcher enhancement for Proc

I'd like to use lambda to spec a method calling in most of the time(e.g change() matcher). I think it would be much more utility if there is a matcher to check if the receiver received any specific message.
For example,

lambda { do something }.should send(receiver, :message).with(any_args()).once

It should be equivalent to:

receiver.should_receive(:message).with(any_args()).once
do something

Suggestion: add 'attempting' and 'attempting_to'

I use the lambda {...}.should syntax quite a bit, but I find it somewhat non-idiomatic. Instead, my first task in every new project is to add the following to my spec_helper.rb file:

def attempting(&block)
  lambda(&block)
end

def attempting_to(&block)
  lambda(&block)
end

I find these pretty convenient, since I now get to write:

it "should raise an error" do
  attempting_to { @sys.write_message :foo }.should raise_error(BigFatError)
end
it "should not raise an error" do
  attempting { @sys.read_from_stdin }.should_not raise_error
end

Just a suggestion that I thought was useful....

Windowed comparison for differences in the middle of long strings

When two strings fail to match, if the difference is somewhere in the middle of the strings, it can be annoying/impossible to track down the actual difference. I've written a little Comparison object that provides this kind of error message:

Strings differ at position 12:
expected: ..."efghijklmnopqrst"...
actual: ..."efghijklXnopqrst"...

It shows a "prelude" of a few characters, then the difference, on successive lines so they're easy to visually scan. It also does the right thing (or tries to) if the difference is near the beginning or end of the string (i.e. does or doesn't show ellipses).

http://gist.github.com/474363

For people who can't wait for this to get incorporated into RSpec proper, you can require comparison.rb and it'll override the existing RSpec == matcher.

One open question is whether the exception message should show the full actual string as well as the comparison... On one hand, it adds to screen clutter, but on the other hand, it can be important in tracking down the problem, especially if the prelude is ambiguous.

Most built in matchers lack cukes

Most of the built in matchers don't have cuke coverage/documentation. I'm going to start working on some of these. I'll add a comment before starting one so we don't duplicate effort.

For anyone else helping out with this: please comment here as well. I think I'll just put comments with links to the commits as I do it, rather than submitting a whole bunch of pull requests.

For starters, I'm going to add cukes for the predicate matchers--i.e. post.should be_valid => assert post.valid?.

What does this mean, maybe it is a bug?

/.rvm/gems/ruby-1.9.2-head/bundler/gems/rspec-expectations-996c752171a0a0e16347e934dadc25767e31186c-master/lib/rspec/expectations/version.rb:4: warning: already initialized constant STRING

When I run rake:cucumber I get this error all of the sudden.

Fix SystemStackError on 1.9.2

myronmarston@ce99ace

I'm having problems opening a pull request for this for some reason. This fixes the spec failures on 1.9.2 that you mentioned earlier today. It was caused by RSpec::Matchers being included multiple times causing its method_missing hook to infinitely super to itself.

Justin's recent contribution to allow other expectations/assertion libraries triggered this. I wasn't sure if you wanted to remove the include from here or there (is it better for rspec-core to take care of including matchers, or matchers to take care of including itself in core?).

Anyhow this fixes the problem, and includes a spec to test that the method_missing supering works properly.

Matcher DSL does not provide a way to define #does_not_match?

We've got match { ... } for #matches? but nothing for #does_not_match?

What should we call it? I was thinking of adding does_not_match { ... }. Or maybe negative_match. Thoughts?

To give you some context for this: I started working on a feature for the include matcher, and found a bug similar to the one in the respond to matcher: [1, 2, 3].should_not include(3, 4) should fail, but currently passes, because matches? returns false. (Since [1, 2, 3].should include(3, 4) should also fail). I need to define does_not_match?, and the include matcher uses the matcher DSL.

hash_including yields false positive when the hash value is an array

Using Ruby 1.8.7-p330 and RSpec 2.5.0.
The following two expectations both pass when they should fail, for a hash instance that has rspec's method_missing mixed in, so the "should_match" method call is resolved:

hash = {:a => 'a'}
hash.should_match hash_including({:a => ['a','b']})
hash.should_match == hash_including({:a => ['a','b']})

The following fails as expected:

hash[:a].should == ['a','b']

Side note: I'm not actually sure why the hash in my original code has the method_missing added that resolves should_match. Just using a literal hash, as written above, does not work:

{:a=>'a'}.should_match ...

results in NoMethodError:
undefined method `should_match' for {:a=>"a"}:Hash

Documentation for raise_error block handler

Edit: see mailing list

I've had a go a writing a scenario for using raise_error with a block:

Commit

Scenario: expect error with block
Given a file named "expect_error_with_block_spec.rb" with:
  """
  describe "accessing expected error" do
    it "should pass the error to the block" do
      my_error = ArgumentError.new
      expect{raise my_error}.to raise_error(ArgumentError) do |raised_error|
        raised_error.should eq my_error
      end
    end
  end
  """
  When I run "rspec ./expect_error_with_block_spec.rb"
  Then the output should contain "1 example, 0 failures"

I wanted to assert that the error passed to the block was the same error that was raised. What do you think? It doesn't seem as straight forward as the previous examples. My concerns are:

  1. I'm using another expectation in a test for the behaviour of a different expectation
  2. The format doesn't quite follow the previous examples of using Object

The features pass and I think it demonstrates the functionality, I'm just not sure about style.

It might be nice if MatchArray worked for Relation, too

When using RSpec with Rails, I made the following 2 specs:

specify { Post.where(:title => 'no such title').should == [] }
specify { Post.where(:title => 'no such title').should =~ [] }

The first spec passes, the other fails. And it fails because I am accidentally testing a Relation instead of an Array (should have called .all before .should, right?).

To avoid this (common?) situation, it might be nice to have =~ implemented on Relation.

`should_not raise_error` annoyingly swallows original exception backtrace

Given a spec like this:

describe "something" do
  it "does not raise an error" do
    expect { something }.to_not raise_error
  end
end

When an error is raised, the spec fails (as it should) but the failure backtrace isn't very useful. The backtrace I care about is for the original error, not the error raised by the raise_error matcher. Really, it would be better to use it("does not raise an error") { something } but the matcher may still be useful in some situations (i.e. to ensure every spec has at least one expectation) and I've got some error reports that have unhelpful backtraces because of this (see this VCR issue, for example).

It would be nice to have the raise_error matcher preserve the original exception backtrace. A few ideas:

  1. Maybe raise_error in the should_not case should be pure syntactic sugar that doesn't actually do anything but execute the block? That way the original error (with associated backtrace) will bubble up and get printed. Users can still use the matcher to make it clear what expectation they are setting for the example.
  2. Alternately, the matcher could print out the original exception's backtrace as part of the failure message. Presumably, we'd want it to respect the full_backtrace configuration option, although we'd need to find a way to do that in such a way that it doesn't couple rspec-expectations to rspec-core.

Myron

expect ... to (vs lambda ... should) needs documentation

I pored over the online docs and never found expect ... to to be documented. Plenty of examples, but nothing that suggested it was preferable over lambda ... should. Even searching for 'expect' in the search box turned up nothing. I admit that I don't understand the structure of rpec well enough to know if this belongs in core documentation or expectations, but it ought to be somewhere.

Don't diff one line strings

The diff in this example adds noise and no value:

"this".should == "that"
....
expected: "that",
     got: "this" (using ==)
Diff:
@@ -1,2 +1,2 @@
-that
+this

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.