I've run into another set of problems with the two solutions you suggested.

If I go the first way, having the macro method define the example inside of 
it, and call that from within a describes block, it appears that any 
instance variables declared in the before block of the spec aren't 
available. Is this correct behavior?

Alternately, if I instead turn it into a matcher and call it from inside an 
it{} block, I'm not able to get proper result messages.

Granted the latter problem is easily ignorable given that the test itself 
works, but I'd like to understand both problems anyway.

Here's the code both ways, first, as a matcher:

# spec/support/api_macros.rb
RSpec::Matchers.define :require_minimum_request_params do |url, params|
  match do |_|
    get url
    response.status.should == 400
    # See https://github.com/dchelimsky/rspec/issues/25
    response.body.include?("missing parameter:")

    (params.length - 1).times do |i|
      params.to_a.combination(i+1).each do |c|
        get url, Hash[*c.flatten]
        response.status.should == 400
        response.body.include?("missing parameter:")
      end
    end

    get url, params
    response.status.should == 200
    !response.body.include?("missing parameter:")
  end
  
  failure_message_for_should do
    "expected URL #{url} to require #{params.keys.join(', ')} as the 
minimum parameters"
  end

  failure_message_for_should_not do
    "expected URL #{url} to not require #{params.keys.join(', ')} as the 
minimum parameters"
  end

  description do
    "require minimum parameters #{params.keys.join(', ')} for requests to 
URL #{url}"
  end
end

# spec/requests/api/api_v1.rb
describe MyApp::API_v1 do
  before do
    @minimum_params = {
      api_key:     "",
      nonce:       "",
      timestamp:   "",
      hmac_digest: ""
    }
  end
  
  context "originator" do
    describe "GET /api/v1/originator/hello" do
      it { should 
require_minimum_request_params("/api/v1/originator/hello", @minimum_params) 
}
    end
  end
end

# $ rspec spec/requests/api/api_v1_spec.rb
MyApp::API_v1
  originator
    GET /api/v1/originator/hello
      should == 200

And instead as a macro:
# spec/support/api_macros.rb
module ApiMacros
  def self.included(base)
    base.extend(ClassMethods)
  end
  
  module ClassMethods
    def it_should_require_minimum_request_params(url, params)
      it "should require minimum request params" do
        get url
        response.status.should == 400
        response.body.should include("missing parameter")
    
        (params.length - 1).times do |i|
          params.to_a.combination(i + 1).each do |c|
            get url, Hash[*c.flatten]
            response.status.should == 400
            response.body.should include("missing parameter")
          end
        end
    
        get url, params
        response.status.should == 200
        response.body.should_not include("missing parameter")
      end
    end
  end
end

# spec/requests/api/api_v1.rb
describe MyApp::API_v1 do
  before do
    @minimum_params = {
      api_key:     "",
      nonce:       "",
      timestamp:   "",
      hmac_digest: ""
    }
  end
  
  context "originator" do
    describe "GET /api/v1/originator/hello" do
      it_should_require_minimum_request_params("/api/v1/originator/hello", 
@minimum_params)
    end
  end
end

# $ rspec spec/requests/api/api_v1_spec.rb
Failure/Error: (params.length - 1).times do |i|
     NoMethodError:
       undefined method `length' for nil:NilClass
     # ./spec/support/api_macros.rb:13:in `block in 
it_should_require_minimum_request_params

On Monday, December 3, 2012 5:42:08 PM UTC-5, Chris Bloom wrote:
>
> Ah, OK. I see the difference now. Thanks for the clarification.
>
> On Monday, December 3, 2012 3:33:42 PM UTC-5, [email protected] wrote:
>>
>> On Mon, Dec 3, 2012 at 2:10 PM, Chris Bloom <[email protected]> wrote: 
>> > I'm trying to refactor some common code used in a bunch of requests 
>> specs 
>> > into a macro, but every way I've tried so far ends in an error saying 
>> it 
>> > can't find the macro method, or if it can then it can't find the `get` 
>> > method. Can someone point me to an example of how to do this? 
>> > 
>> > # spec/requests/api/api_v1.rb 
>> > describe MyApp::API_v1 do 
>> >   context "originator" do 
>> >     describe "GET /api/v1/originator/hello" do 
>> >       it_should_check_minimum_protected_api_params 
>> > "/api/v1/originator/hello" 
>> >     end 
>> >   end 
>> > end 
>> > 
>> > # spec/support/api_macros.rb 
>> > module ApiMacros 
>> >   def self.included(base) 
>> >     base.extend(GroupMethods) 
>> >   end 
>> > 
>> >   module GroupMethods 
>> >     def it_should_check_minimum_protected_api_params(url) 
>> >       get url 
>> >       ... 
>> >     end 
>> >   end 
>> > end 
>> > 
>> > # spec/spec_helper.rb 
>> > RSpec.configure do |config| 
>> >   config.include ApiMacros, :type => :request 
>> > end 
>> > 
>> > This ends in: 
>> > 
>> > $ rspec spec/requests/api 
>> > /spec/support/api_macros.rb:8:in 
>> > `it_should_check_minimum_protected_api_params': undefined method `get' 
>> for 
>> > #<Class:0x000001036708f0> (NoMethodError) 
>>
>> That's not saying it can't find the macro method. It says it can't find 
>> `get`. 
>>
>> The macro is being evaluated at the class level, whereas "get" is an 
>> instance method. The macro needs to define examples that use the get 
>> method, e.g: 
>>
>> def it_should_check_minimum_protected_api_params(url) 
>>   it "should check minimum protected api params" do 
>>     get url 
>>     # ... 
>>   end 
>> end 
>>
>> HTH, 
>> David 
>>
>

-- 
You received this message because you are subscribed to the Google Groups 
"rspec" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msg/rspec/-/R3BPlOkxEZIJ.
For more options, visit https://groups.google.com/groups/opt_out.


Reply via email to