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] 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.
>
>
--
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.