BTW: Thank you for your feedback so far!

On Wed, Dec 5, 2012 at 9:04 PM, Chris Bloom <[email protected]> wrote:

> I like the idea of breaking it out into small sub-tests, but I think both
> of the issues I mentioned previously would still be present. That is, it
> doesn't appear that the before block is executed for macros, so instance
> variables from my setup code aren't available, and if I write it as a
> matcher I still won't get the correct test result messages. I need to test
> this behavior for an indeterminate number of API endpoints, which is why I
> went with a macro in the first place.
>
>
> On Tue, Dec 4, 2012 at 4:21 PM, David Chelimsky <[email protected]>wrote:
>
>> Didn't realize you were trying to do so much in one statement.
>>
>> The idea of a matcher, custom or built-in, is that the match block has
>> one expectation expressed as a boolean - it should return true or
>> false to indicate pass or fail. This one matcher is wrapping 10
>> different expectations in logical pairs. I'd probably start with 5
>> examples with two expectations in each:
>>
>> it "is valid with the minimum params" do
>>   get "/api/v1/originator/hello", @minimum_params
>>   expect(response.status).to eq(200)
>>   expect(response.body).not_to include("missing parameter:")
>> end
>>
>> it "requires api_key in params" do
>>   get "/api/v1/originator/hello", @minimum_params.except("api_key")
>>   expect(response.status).to eq(400)
>>   expect(response.body).to include("missing parameter: api_key")
>> end
>>
>> # 3 more failure cases
>>
>> Each example has two expectations, but they work together to specify
>> different parts of the same outcome, so I'm comfortable bypassing the
>> one-expectation-per-example guideline.
>>
>> You could, conceivably, reduce some of the duplication with a custom
>> matcher that just deals with one parameter - something like:
>>
>> it { should require_param("api_key") }
>>
>> Either that or wrap the failure examples in an an iterator:
>>
>> describe "minimum params" do
>>   MIN_PARAMS = {
>>     api_key:     "",
>>     nonce:       "",
>>     timestamp:   "",
>>     hmac_digest: "
>>   }
>>
>>   MIN_PARAMS.each_pair do |k, v|
>>     it "requires api_key in params" do
>>       get "/api/v1/originator/hello", MIN_PARAMS.except(k)
>>       expect(response.status).to eq(400)
>>       expect(response.body).to include("missing parameter: #{k}")
>>     end
>>   end
>> end
>>
>> WDYT?
>>
>> On Tue, Dec 4, 2012 at 2:17 PM, Chris Bloom <[email protected]>
>> wrote:
>> > 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]:
>> >>>
>> >>> 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.
>> >
>> >
>>
>> --
>> 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].
>> For more options, visit https://groups.google.com/groups/opt_out.
>>
>>
>>
>

-- 
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].
For more options, visit https://groups.google.com/groups/opt_out.


Reply via email to