I'll chime in, having contributed some of the mess at hand. Good things I'm seeing between current route helpers and proposals include:
* The router being at the center of what's being tested * Similarity of specs to other conventions * Ability to specify bi-directional routing behavior (by default, since it's most common) * Clear indication that a route spec is in fact testing both recognition and generation * Easily specify negative routes (PUT /foo doesn't route). * Include testing of URL helpers * DRY, but not at the expense of clarity I'm uncertain about the need to easily specify one-directional routes. While in theory it sounds fine, I don't understand why you'd want to specify either a route that isn't recognized (why bother routing it, in this case?) or one that doesn't generate the given path with url_for() (does it generate some other path instead?). I didn't see any examples of its usage in the gist, but I did see a lot of code for it... YAGNI?? or, if I were able to understand the motivation better, maybe this would make more sense to me. Randy On 7/5/10 9:18 AM, David Chelimsky wrote: > On Jul 5, 2010, at 9:04 AM, Wincent Colaiuta wrote: > > >> El 05/07/2010, a las 13:56, David Chelimsky escribió: >> >> >>> Nice overall. Much of the code belongs in Rails, though, so I'd like to try >>> to get a patch in to Rails once we get this worked out. I'd like the >>> rspec-rails matchers to be simple wrappers for rails' assertions wherever >>> possible. >>> >> >> Well, I'm not sure if there is a problem with the Rails assertions. >> > Not that there is a problem with them, but all Rails users could benefit from > methods like get(path) in their routing tests. Although, now that I think of > it, those assertions are available in functional tests, and the http verbs > are already defined there. > > The thing that concerns me the most is the DestinationParser. Even though it > seems simple, that's the sort of code that ends up making rspec-rails a > rails-dependent maintenance problem. > > >> In my (albeit limited) experience I haven't yet seen a situation where they >> couldn't express something during testing. So as you point out, the job of >> RSpec should just be to expose those APIs within specs in a usable and >> effective manner. If we discover some limitation that we can't get around, >> then sure, the upstream assertions will need some work. >> >> >>> I think having three separate matchers would land us in a good spot. >>> >> Agreed. That's why I added a matcher for each of assert_recognizes, >> assert_generates, and assert_routing (although so far in practice I've only >> had to use two of them). >> >> >>> The wrapped rails assertions also accept a hash representing query params, >>> so let's see if we can accommodate that as well: >>> >>> get('/something', :ref_id => '1234').should route_to(....) >>> >> Yes, in my initial implementation I left that out seeing as I haven't yet >> run into a spec that needed it, but I did have it in mind when I wrote the >> "get" etc methods (in fact, when I first typed them in I started with 'def >> get path, options = {}', but then thought, YAGNI, better not add that in >> until I am sure I need it...) >> >> >>> Something like: >>> >>> describe "The router" do >>> it { should recognize(get('/wiki/foo')).as("wiki#show", :id => 'foo') } >>> it { should generate(get('/wiki/foo')).from("wiki#show", :id => 'foo') } >>> it { should route(get('/wiki/foo')).to("wiki#show", :id => 'foo') } >>> end >>> >> Yes, the "action#controller" notation is a good idea. I actually added that >> to the gist that I posted already. >> > That's where I got it from :) > > >> Not sure about nesting the get() method inside the matcher though. It seems >> that in most other places, we use hashes or literal values; eg: >> >> something.should do_something(:when => 'always', :how => 'quickly') >> >> So, it would be more consistent with existing style to write: >> >> it { should recognize(:get => '/wiki/foo').as('wiki#show', :id => 'foo') } >> >> But then we lose some of the conciseness I was looking for in my original >> proposal. >> > Keep in mind this is for a one-liner (no string passed to it), so it's still > far more concise than the previous alternatives. > > >> In any case we also lose the similarity with controller specs (where "get" >> is the first thing on the line) which I was also looking for. >> >> Nevertheless, your choice of verbs and prepositions work well together; >> "recognize as", "generate from" and "route to" all read fairly well. >> >> Just to clarify, are you suggesting that: >> >> - "recognize as" wrap assert_recognizes? >> - "generate from" wrap assert_generates? >> - "route to" wrap assert_routing? >> >> If so, one misgiving I have is that we still have a unidirectional >> preposition ("to") being used in conjunction with the bidirectional >> assertion. >> > Agreed. Not sure what the right verbiage is there. > > >> An interesting consequence of the language you propose is that the focus of >> testing changes ie. in the current generator templates, we have: >> >> describe FooController do >> describe 'routing' do >> it 'recognizes and generates #index' do >> { :get => 'bar' }.should route_to(:controller => 'bar', :action => >> 'index') >> >> So the focus is on controller actions, and the subject of each 'it' block is >> a literal hash representing a request to that controller and action. >> >> The new language puts the router at the center and the implicit subject of >> each 'it' block is really the router itself. >> > I think that's where it belongs. It's the router that does the routing, not > the controller. > > >>> That's definitely nice and clean, and it makes it easier to align the names >>> with the rails assertions, which I think adds some value. The output could >>> be formatted something like this: >>> >>> The router >>> should recognize get('/wiki/foo') as wiki#show with :id => 'foo' >>> should generate get('/wiki/foo') from wiki#show with :id => 'foo' >>> should route get('/wiki/foo') to wiki#show with :id => 'foo' >>> >>> Thoughts? >>> >> I think it would be nicer to format that as "GET /wiki/foo" (ie. HTTP verb >> followed by path) rather than embedding the literal text of the >> "get('/wiki/foo')" method call. >> > You could still get that by adding a message: > > it { should recognize(get('/wiki/foo')).as("wiki#show", :id => 'foo'), > "recognizes GET /wiki/:id" } > > Not saying that's any better than the other formats, just that it exists as > an option. > > Slight tangent - one nice thing about 'recognize' as a matcher name is we get > this for free: > > it { should_not recognize(:get => '/wiki/foo') } > > >> In any case, I converted another RESTful resource over to the syntax I >> mentioned earlier, now using the "controller#action" syntax: >> >> describe IssuesController do >> describe 'routing' do >> example 'GET /issues' do >> get('/issues').should map('issues#index') >> end >> >> example 'GET /issues/new' do >> get('/issues/new').should map('issues#new') >> end >> >> example 'GET /issues/:id' do >> get('/issues/123').should map('issues#show', :id => '123') >> end >> >> example 'GET /issues/:id/edit' do >> get('/issues/123/edit').should map('issues#edit', :id => '123') >> end >> >> example 'PUT /issues/:id' do >> put('/issues/123').should map('issues#update', :id => '123') >> end >> >> example 'DELETE /issues/:id' do >> delete('/issues/123').should map('issues#destroy', :id => '123') >> end >> end >> end >> >> Which, if you really care about conciseness, can be written as: >> >> describe IssuesController do >> describe 'routing' do >> it { post('/issues').should map('issues#create') } >> it { get('/issues').should map('issues#index') } >> it { get('/issues/new').should map('issues#new') } >> it { get('/issues/123').should map('issues#show', :id => '123') } >> it { get('/issues/123/edit').should map('issues#edit', :id => '123') } >> it { put('/issues/123').should map('issues#update', :id => '123') } >> it { delete('/issues/123').should map('issues#destroy', :id => '123') } >> end >> end >> > The point of "it' is that it reads as part of a sentence: > > describe "something" do > it "does something" > > When we introduced the implicit subject, we got this: > > describe "something" do > it { should do_something } > > In that form it still reads nicely: "it should do something". > > In this case, saying "it get post issues should map issues#create" makes me > want to cry. We've still got example and specify. In this case, I think > specify is the most accurate: > > describe IssuesController do > describe 'routing' do > specify { post('/issues').should map('issues#create') } > > Out loud this is "specify [that a] post [to] /issues should map [to] > issues#create". I can live with that tear-free. Except when Brazil gets > knocked out of the cup, but that's a different matter. > > >> These get formatted in the spec output as: >> >> IssuesController >> routing >> GET /issues >> GET /issues/new >> GET /issues/:id >> GET /issues/:id/edit >> PUT /issues/:id >> DELETE /issues/:id >> >> And: >> >> IssuesController >> routing >> should map "issues#create" >> should map "issues#index" >> should map "issues#new" >> should map "issues#show" and {:id=>"123"} >> should map "issues#edit" and {:id=>"123"} >> should map "issues#update" and {:id=>"123"} >> should map "issues#destroy" and {:id=>"123"} >> >> Respectively. By adding a description method to the matcher (see updated >> Gist: http://gist.github.com/464081), we can produce: >> >> IssuesController >> routing >> should map POST /issues as issues#create >> should map GET /issues as issues#index >> should map GET /issues/new as issues#new >> should map GET /issues/123 as issues#show with {:id=>"123"} >> should map GET /issues/123/edit as issues#edit with {:id=>"123"} >> should map PUT /issues/123 as issues#update with {:id=>"123"} >> should map DELETE /issues/123 as issues#destroy with {:id=>"123"} >> >> Which IMO looks pretty nice. >> > It does, though seeing that last output makes me wonder about the "map/as" > verbiage. Seems less clear in this context for some reason. > > Anybody else besides me and Wincent and Matt want to weigh in here? > > Cheers, > David > _______________________________________________ > 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