On Wed, Jun 17, 2009 at 6:54 PM, <r_j_h_box...@yahoo.com> wrote:
>
>
> Okay, after such a harsh analysis of the problem, I figured it was worth 
> digging in just a little bit more.  Side note, some of what I was seeing 
> yesterday was a by-product of having default routes still existing.  But 
> there are still some essential facts that remain, which I present here.
>
> 1. The current implementation of route_for().should == "/something" is only 
> consulting route recognition. A more descriptive phrasing of this behavior 
> would be something like
>
>>   route_recognize("/something").should == { :action ... :controller ... }
>
> 2. of course, params_for() was *also* consulting route recognition through a 
> slightly different code path, but eventually using exactly the same test.
>
> Therefore, route generation and recognition are a) redundant and b) 
> incomplete.
>
>
> I did dig in more to the problem and spiked out a fairly solid
> alternative, which is 100% backward-compatible, plus a bunch.  Here's
> my thinking process:
>
> 1. If we were to use assert_generates(), it could test that the params result 
> in the specified path, the way implied by the current route_for().should == 
> syntax.  Note, however, that it doesn't verify that the :method arg is 
> correct (if the hash with :path, :method is provided, it actually causes 
> failure).
>
> 2. assert_recognizes() does test the method of { :path ... :method  }
>
> Therefore, if we can do both assert_generate() and assert_recognizes(), then 
> we have covered testing a) actual route generation, b) method matching, and 
> c) (bonus result) params_for() matching.  This last is nice, though kind of a 
> hidden benefit that doesn't make itself obvious to the reader of the briefer 
> routing spec.  Fortunately, it's optional - as I said, what I've done is 100% 
> back-compatible.
>
>
> Also, to support my original goals, some methods (which require catching 
> exceptions that would otherwise be informative) to provide:
>
> 1.  Non-routeability tests (:action ... :controller).should_not be_routable
>
> 2.  Path-cleanliness and negative method tests
>
>> route_for(:action ... :controller ... :args ).should_not be_post("/path")
>
> Hey, did you catch that?  be_post(), I said.  Well, because it catches 
> exceptions, it's not so useful for positive cases.  So:
>
> 42.  A few extra methods that directly test expected positive cases, which 
> can (but don't have to) replace .should == { :path ... :method }
>
>> route_for(:action ... :controller ... :args ).should post("/path")
>> route_for(:action ... :controller ... :args ).should put("/path")
>> route_for(:action ... :controller ... :args ).should get("/path")
>> route_for(:action ... :controller ... :args ).should delete("/path")
>
> This gist demonstrates a currently-working example of this method, annotated 
> to introduce things step by step, including the results of my spike that 
> makes it all work.
>
> http://gist.github.com/131569
>
> I hope that this is both more helpful than just my griping of yesterday, and 
> that it adds some value to the project.

This is all good stuff Randy. Thanks.

Wanna make it a lighthouse ticket with patch? We can talk about the
get/put/post/delete-ish methods there.

Cheers,
David

>
> Randy
>
>
>
> ----- Original Message ----
>> From: "r_j_h_box...@yahoo.com" <r_j_h_box...@yahoo.com>
>> To: rspec-users <rspec-users@rubyforge.org>
>> Sent: Tuesday, June 16, 2009 5:23:34 PM
>> Subject: Re: [rspec-users] Problem verifying routing error
>>
>>
>> Here's another interesting symptom.  After tracing through the code, I've 
>> come
>> to the understanding that the current implementation (delegated to outside
>> rspec, I understand) of route "generation" is not
>> testing generation at all, but rather is using backward-recognition as a 
>> proxy.
>> Further, that recognition doesn't correspond to what the real router does
>> recognize.
>>
>> For clarity, here's the background: A resource that requires nesting for new,
>> create; requires
>> no nesting for edit, update, destroy, and has no index or show.
>>
>> >  map.resources :designs, :only => [:edit, :update, :destroy]
>> > map.resources :product, :member => { :redraw => :get } do |product|
>> >   product.resources :designs, :member => { :set_default =>
>> :put }, :except => [ :edit, :update, :destroy, :index, :show ]
>> > end
>>
>> Okay: when I go to /designs/new in the browser, it borks with a RoutingError.
>> That's the way I want it to behave, real-world.  Yet, this fails:
>>
>> > expect { route_for(:controller => "designs", :action => "new") ==
>> "/designs/new" }.to raise_error( ActionController::RoutingError )
>>
>> There's no error raised at all here.
>>
>> The following does gripe, but... what it's *really* griping about (in a 
>> hidden
>> way) is "bogus path", not about the route_for() params at all.
>>
>>
>> > expect {  route_for(:controller => "designs", :action
>> => "new") == "bogus path" }.to raise_error(
>> ActionController::RoutingError )
>>
>> (so if we replace route_for([bad]) with route_for([good]) == "bogus path", 
>> then
>> we still get the routing error.
>>
>>
>> Furthermore, the first one really recognizes the route string (/designs/new),
>> without actually verifying that there is a route in the routing table for it.
>> So I fear that it's not actually testing what I'm asking it to test.  Taking 
>> it
>> out of the expect {} *does* make it barf, but with evidence that something is
>> just plain confused, not that it's actually testing what we're asking it to
>> test:
>>
>> [wrapping is mine]
>> > The recognized options <{"action"=>"1", "controller"=>"designs"}>
>> > did not match <{"action"=>"show", "id"=>"1", "controller"=>"designs"}>,
>> > difference: <{"action"=>"show", "id"=>"1"}>
>>
>> At the end of the day, what I find is:
>>
>> * Route generation tests are not testing generation at all, but recognition 
>> only
>> * They're only testing recognition of ideal cases
>> * Non-existence of routes is currently not testable with rspec
>>
>> I hoped to just assert something on url_for() - that's the practical
>> application, here.  Does, or does not, url_for() produce a useful result with
>> specific args?  But I see from ActionController::Base how that's not super
>> practical.
>>
>> I sincerely hope that my understanding is wildly mistaken.
>>
>> Sorry if this is a sore spot; I know that this part has been a lot of painful
>> effort so far, far more for others than for myself.  I'll end with an 
>> expression
>> of deep and sincere appreciation for this great software.
>>
>> Randy
>>
>>
>>
>>
>> ----- Original Message ----
>> > From: "r_j_h_box...@yahoo.com"
>> > To: rspec-users
>> > Sent: Tuesday, June 16, 2009 2:14:52 PM
>> > Subject: Re: [rspec-users] Problem verifying routing error
>> >
>> >
>> > David, thank you for your reply on this.  I really dig the expect { }.to
>> > raise_error() syntax!!
>> >
>> > To clarify: All the things you're claiming match my expectation.
>> Unfortunately,
>> > my expectation does not match reality according to my tests.
>> >
>> > The thing is, route_for([bad stuff]) does not in and of itself raise a 
>> > routing
>>
>> > error.  It constructs an object that hasn't yet been compared with == to
>> > anything.
>> >
>> > 23   t = route_for(:controller => "designs", :action => "create")
>> >
>> > (rdb:1) puts t
>> > #
>> >
>> > According to my tests, the routing error only occurs after route_for()'s
>> result
>> > gets compared to something.  So lambda { route_for(...) } does not raise
>> error.
>> >
>> > The following code passes with flying colors, either in lambda or expect 
>> > {}.to
>>
>> > form:
>> >
>> >     t = route_for(:controller => "designs", :action => "create")
>> >     expect { t == "anything" }.to raise_error( 
>> > ActionController::RoutingError
>> )
>> >     expect { t.should == "anything" }.to raise_error(
>> > ActionController::RoutingError )
>> >
>> > Any further ideas?
>> >
>> > Randy
>> >
>> >
>> >
>> >
>> >
>> > ----- Original Message ----
>> > > From: David Chelimsky
>> > > To: rspec-users
>> > > Sent: Tuesday, June 16, 2009 1:28:18 PM
>> > > Subject: Re: [rspec-users] Problem verifying routing error
>> > >
>> > > On Tue, Jun 16, 2009 at 3:07 PM, wrote:
>> > > >
>> > > > I finally figured this out.
>> > > >
>> > > > lambda { route_for(:controller => "designs", :action => 
>> > > > "create").should
>> ==
>> > > "anything" }.should raise_error( ActionController::RoutingError )
>> > > >
>> > > > The clue was that I wasn't getting a routing error until I tried to
>> compare
>> > > route_for() with something.  route_for() seems to generate an object that
>> > > overrides ==(), and at that time it does raise the exception.  Now we 
>> > > wrap
>> > that
>> > > comparison in a lambda and assert that the *comparison* should raise the
>> > > expected routing error.
>> > > >
>> > > > So - great, we can actually test it.  But the syntax does leave 
>> > > > something
>> to
>> >
>> > > be desired.  dchelimsky, can you recommend any alternatives that would 
>> > > be a
>> > bit
>> > > cleaner for testing that a route doesn't exist?
>> > > >
>> > >
>> > > You don't need the .should == "anything" in there. So this is a bit 
>> > > cleaner:
>> > >
>> > >   lambda { route_for(:controller => "designs", :action => "create")
>> > > }.should raise_error( ActionController::RoutingError )
>> > >
>> > > Also, since rspec-1.2.5 you can use expect/to:
>> > >
>> > >   expect { route_for(:controller => "designs", :action => "create")
>> > > }.to raise_error( ActionController::RoutingError )
>> > >
>> > > You could always kick it old-school:
>> > >
>> > >   e = nil
>> > >   begin
>> > >     route_for(:controller => "designs", :action => "create")
>> > >   rescue ActionController::RoutingError => e
>> > >   ensure
>> > >     e.should_not be_nil
>> > >   end
>> > >
>> > > And you could always wrap this in an new matcher:
>> > >
>> > > def be_routable
>> > >   Spec::Matchers.new :be_routable, self do |example|
>> > >     match do |params|
>> > >       e = nil
>> > >       begin
>> > >         example.route_for(params)
>> > >       rescue ActionController::RoutingError => e
>> > >       end
>> > >       !!e
>> > >     end
>> > >   end
>> > > end
>> > >
>> > > {:controller => "designs", :action => "create"}.should_not be_routable
>> > >
>> > > In this case you need to wrap the matcher's construction in a method
>> > > in order to provide access to the scope of the example (which is where
>> > > route_for lives). Also, I just whipped that up off the top of my head
>> > > - no idea if it actually works :)
>> > >
>> > > HTH,
>> > > David
>> > >
>> > >
>> > >
>> > >
>> > > > Thanks,
>> > > >
>> > > > Randy
>> > > >
>> > > >
>> > > >
>> > > >
>> > > > ----- Original Message ----
>> > > >> From: Ben Mabey
>> > > >> To: r_j_h_box...@yahoo.com; rspec-users
>> > > >> Sent: Friday, May 8, 2009 10:25:03 AM
>> > > >> Subject: Re: [rspec-users] Problem verifying routing error
>> > > >>
>> > > >> Randy Harmon wrote:
>> > > >> > Hi,
>> > > >> >
>> > > >> > When upgrading to rspec/rspec-rails 1.2.6 gem (from 1.1.12), I'm 
>> > > >> > having
>> > > >> > a new problem verifying routes that should not exist.
>> > > >> >
>> > > >> > This is to support something like this in routes.rb:
>> > > >> >
>> > > >> > map.resources :orders do |orders|
>> > > >> >     orders.resources :items, :except => [:index,:show]
>> > > >> > end
>> > > >> >
>> > > >> > I used to use lambda {}.should_raise( routing error ), but it 
>> > > >> > stopped
>> > > >> > detecting any raised error.  Requesting it through the browser 
>> > > >> > produces
>> > > >> > ActionController::MethodNotAllowed (Only post requests are allowed).
>> But
>> > > >> > that error wasn't detected.
>> > > >> >
>> > > >> > When I skip the lambda, and just ask it to verify that the route 
>> > > >> > does
>> > > >> > exist (which *should* fail), I get the same result for those :except
>> > > >> > actions as for a made-up action name.  Seems this must have 
>> > > >> > something
>> to
>> > > >> > do with the change in how route_for delegates back to
>> ActionController's
>> > > >> > routing assertion (sez the backtrace :).
>> > > >> >
>> > > >> >
>> > > >> > NoMethodError in 'ItemsController route generation should NOT map
>> > > >> > #indewfefwex'
>> > > >> > You have a nil object when you didn't expect it!
>> > > >> > You might have expected an instance of Array.
>> > > >> > The error occurred while evaluating nil.first
>> > > >> >
>> > > >>
>> > >
>> >
>> /Library/Ruby/Gems/1.8/gems/actionpack-2.2.2/lib/action_controller/assertions/routing_assertions.rb:134:in
>> > > >> > `recognized_request_for'
>> > > >> >
>> > > >>
>> > >
>> >
>> /Library/Ruby/Gems/1.8/gems/actionpack-2.2.2/lib/action_controller/assertions/routing_assertions.rb:49:in
>> > > >> > `assert_recognizes'
>> > > >> >
>> > > >>
>> > >
>> >
>> /Library/Ruby/Gems/1.8/gems/actionpack-2.2.2/lib/action_controller/assertions.rb:54:in
>> > > >> > `clean_backtrace'
>> > > >> >
>> > > >>
>> > >
>> >
>> /Library/Ruby/Gems/1.8/gems/actionpack-2.2.2/lib/action_controller/assertions/routing_assertions.rb:47:in
>> > > >> > `assert_recognizes'
>> > > >> > ./spec/controllers/thoughts_routing_spec.rb:9:
>> > > >> >
>> > > >> >
>> > > >> > I tried using bypass_rescue in my routing/items_routing_spec.rb 
>> > > >> > file as
>> > > >> > mentioned by the upgrade doc, but it wasn't valid in the "routing" 
>> > > >> > spec
>> > > >> > - worked fine when I moved the file back to spec/controllers/, 
>> > > >> > though.
>> > > >> > Seems like that's not the issue, but I'm mentioning for more
>> > completeness.
>> > > >> >
>> > > >> > Any ideas what I should be doing instead, or how I can troubleshoot
>> > > further?
>> > > >> >
>> > > >>
>> > > >>
>> > > >> Hmm.. yeah, it seems like it might have to do with how the exceptions
>> > > >> are being handled in the newer version of rspec-rials (see
>> > > >>
>> > >
>> >
>> https://rspec.lighthouseapp.com/projects/5645/tickets/85-11818-have-mode-for-rails-error-handling).
>> > > >>
>> > > >> I don't use RSpec to verify my routes very often and have never used 
>> > > >> it
>> > > >> to verify the non-existence of a route so I'm afraid I don't really 
>> > > >> have
>> > > >> any ideas...
>> > > >>
>> > > >> Does anyone else have an idea to do this?
>> > > >>
>> > > >> -Ben
>> > > >
>> > > > _______________________________________________
>> > > > rspec-users mailing list
>> > > > rspec-users@rubyforge.org
>> > > > http://rubyforge.org/mailman/listinfo/rspec-users
>> > > >
>> > > _______________________________________________
>> > > rspec-users mailing list
>> > > rspec-users@rubyforge.org
>> > > http://rubyforge.org/mailman/listinfo/rspec-users
>> >
>> > _______________________________________________
>> > rspec-users mailing list
>> > rspec-users@rubyforge.org
>> > http://rubyforge.org/mailman/listinfo/rspec-users
>>
>> _______________________________________________
>> rspec-users mailing list
>> rspec-users@rubyforge.org
>> http://rubyforge.org/mailman/listinfo/rspec-users
>
> _______________________________________________
> rspec-users mailing list
> rspec-users@rubyforge.org
> http://rubyforge.org/mailman/listinfo/rspec-users
>
_______________________________________________
rspec-users mailing list
rspec-users@rubyforge.org
http://rubyforge.org/mailman/listinfo/rspec-users

Reply via email to