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