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.
