[rspec-devel] [ rspec-Feature Requests-8771 ] Spec::Mocks::BaseExpectation#with converts hash params to array of arrays with #collect

noreply at rubyforge.org noreply at rubyforge.org
Tue Feb 20 06:03:22 EST 2007

Feature Requests item #8771, was opened at 2007-02-20 00:07
You can respond by visiting: 

Category: mock module
Group: None
>Status: Closed
Priority: 3
Submitted By: James Hughes (jpath)
>Assigned to: David Chelimsky (dchelimsky)
Summary: Spec::Mocks::BaseExpectation#with converts hash params to array of arrays with #collect

Initial Comment:
This setup block:

setup do
     @user = mock("user")
     @params = {:cn => "Bilbo Baggins",
      :telephoneNumber => "416-277-4418",
      :mail => "bilbo at baggins.com"}

with this spec:

specify "should update and save the attributes for user" do
    post :update, :id => "jhughes", :user => @params

and this controller action:

  def update
    @user = User.find params[:id]
    @user.update_attributes params[:user]
    redirect_to :action => 'list'

results in this failure: 

Mock 'user' expected :update_attributes with ([:telephoneNumber, "416-277-4418"], [:mail, "bilbo at baggins.com"], [:cn, "Bilbo Baggins"]) but received it with ({"cn"=>"Bilbo Baggins", "telephoneNumber"=>"416-277-4418", "mail"=>"bilbo at baggins.com"

As pointed out by Jerry West on rspec-users, the hash that is passed to #with is getting Enumerable#collect called on it and converted to an array of arrays.
To quote Jerry: "If you check out $GEM_DIR/rspec- you will see that #with() passes your hash to a new instance of ArgumentExpectation, which is defined in $GEM_DIR/rspec- .   There you will see that a hash is a LiteralArgConstraint (as opposed to :anything, :numeric, and the other parameters that #with() accepts).  The interesting code is around line 89 in #process_arg_constraints() where your hash is converted one item at a time using the normal Ruby Enumerable routine #collect().  Which is where the side-effect happens."

I'm running off of trunk, rev. 1514 as of this writing.

Note that I attempted to do a "bug reduction" with this, distilling things down to the simplest thing that would produce the same error (i.e. without all the rails stuff), and I couldn't get it to work. This is what I came up with:

class Test
  def Test.amethod params
context "passing a hash to #with" do
  specify "should pass a hash to #with" do
    @params = { :param1 => "foo",
      :param2 => "bar"}
    Test.amethod @params

This passes. My rspec/mock object-fu is not sufficient to spot the difference between the two examples.


>Comment By: David Chelimsky (dchelimsky)
Date: 2007-02-20 11:03

Fixed in trunk rev (1515)

The problem has two parts. Pass/Fail and message.

It turns out that pass/fail is working correctly. The spec is failing because Rails stringifies the keys in the Hash.

The RSpec bug here is in the message, not whether or not it passes or fails.

I've fixed the message so it will show you the two hashes (expected and actual), but I think that getting RSpec to make decisions about stringifying keys would be a mistake. To that end, I think the right thing is for you to change your spec to expect a Hash with stringy keys.

Feel free to take this up on the mailing list if you disagree.



You can respond by visiting: 

More information about the rspec-devel mailing list