RSpec Routing DSL

I started testing routes for the first time in Rails 3 this weekend
during Rails Rumble. I was so exhausted that I found writing route
specs a very painful task. I came up with my own routing DSL and I’d
love to see it get included in RSpec itself. Before I start adding the
code to rspec-rails, I’d like to get some feedback and see if there
are some ways we could clean it up. Basically the DSL looks like:

describe “My routes” do

get “/blog” => { controller: “blogs-controller”, action: “index” }

end

You can see all the details and the module used to make it work here:
630176’s gists · GitHub. Thoughts?

On Oct 18, 2010, at 1:20 PM, Joe F. wrote:

end

You can see all the details and the module used to make it work here:
630176’s gists · GitHub. Thoughts?

Hey Joe - I think this is really nice, but it’s unlike any other
matchers/macros that ship with RSpec, so I don’t feel it belongs in
RSpec as it stands today. If future versions move in a direction that
better aligns with this we can revisit.

That said, I think it’s a nice clean DSL and recommend that you publish
it as a gem and share it with those who wish to use it.

In terms of improving it, the only thing I see missing is a way of
specifying a route that is not supported. Maybe something like:

get “/blog” => unroutable

WDYT?

El 18/10/2010, a las 20:20, Joe F. escribi:

end

You can see all the details and the module used to make it work here:
630176’s gists · GitHub. Thoughts?

I felt the same pain a while back and proposed a DSL too, but it never
really got anywhere as there was no consensus about what a new DSL
should look like. Full thread here:

http://groups.google.com/group/rspec/browse_thread/thread/50b46ca3e4bd3a78/da928456061063c6

I never got as far as submitting a patch because I didn’t really like
the alternative proposals so wasn’t going to code them up (I’d already
posted my own working proposal).

After several iterations, the implementation that I am currently using
consists of “map_to”, “map_from”, “have_routing” (ie. map both ways) and
“be_recognized” matchers; these were chosen largely because they don’t
clash with the existing matchers in RSpec and so I can use them on an
“opt-in” basis:

routing_example_group_helpers.rb · GitHub

Some sample specs:

articles_routing_spec.rb · GitHub

One thing to note is how there are two assertions in there where I use
“map_to” instead of “have_routing” because of what looks to be a bug in
the Rails routing assertion macros. I think there is a Lighthouse ticket
for this but the only ones related to “assert_generates” which I can
find right now are:

#5260 routing assertions do not support constraints - Ruby on Rails - rails
#5005 Error on assert_recognizes when used on a route with constraint - Ruby on Rails - rails
#5689 assert_generates expects unexpected generated_path - Ruby on Rails - rails

At least one of those issues (#5260, #5005) is supposedly resolved in
3.0.2. #5698 was marked as invalid. Tangentially related is this old
ticket which I posted:

Lighthouse - Beautifully Simple Issue Tracking

I thought someone posted a pretty good analysis of exactly what the
breakage is and why it happens, but I can’t find it. :frowning: Guess when I
get time will have to do some analysis of the Rails codebase and figure
out what’s happening and put together another ticket.

While Googling, found this, however, describing changes in 2.0.0:

API for uni-directional routing expectations · Issue #221 · rspec/rspec-rails · GitHub

Which notes that RSpec’s “route_to” now delegates to “assert_recognizes”
(a one-way assertion) rather than “assert_routing” (a two-way
assertion).

Cheers,
Wincent

On Oct 19, 2010, at 2:05 AM, Wincent C. wrote:

I thought someone posted a pretty good analysis of exactly what the breakage is
and why it happens, but I can’t find it. :frowning: Guess when I get time will have to do
some analysis of the Rails codebase and figure out what’s happening and put
together another ticket.

While Googling, found this, however, describing changes in 2.0.0:

API for uni-directional routing expectations · Issue #221 · rspec/rspec-rails · GitHub

Which notes that RSpec’s “route_to” now delegates to “assert_recognizes” (a
one-way assertion) rather than “assert_routing” (a two-way assertion).

The problem with the bi-directional expectation was that you can have
two routes that map to the same path:

resources :widgets
root :to => “widgets#index”

In this case, both of these are true if all we expect is the route
recognition:

{ :get => “/” }.should route_to(:controller => “widgets”, :action =>
“index”)
{ :get => “/widgets/index” }.should route_to(:controller => “widgets”,
:action => “index”)

However, route generation would not generate “/” from (:controller =>
“widgets”, :action => “index”). Assuming a bi-directional mapping for
every case was wrong, so I changed it to uni-directional (route
recognition).

The other 1/2 of the motivation for this change was that route
generation is something that is well specified and tested in Rails
itself. In our apps, our specs should spec things like “generates a link
to the widget,” not “builds a link using the widget.” So I don’t see
much value in expectations about generation, and I certainly don’t see
them as a routing concern any longer. They may be so within the rails
framework, but they are unrelated to what I’m specifying when I specify
routes. Make sense?

So for me, all we need is uni-directional expectations for routes we
want to exist and routes we want to not exist, hence:

{ :get => “/” }.should route_to(:controller => “foo”, :action =>
“bar”)
{ :get => “/private_stuff” }.should_not be_routable

I would definitely be open to adding conveniences to clean this up:

get(“/”).should route_to(“foo#bar”)
get(“/private_stuff”).should_not be_routable

Then Joe’s DSL could exploit those and we’d get:

get “/blog” => “blogs-controller#index”

Still not sure about the negative (unroutable).

Cheers,
David

David: you’re right, it’s not very rspecish :slight_smile: There are two changes I
made that I wonder if we could see in core are:

  1. printing out the path and method in specdoc output
  2. setting default scope options per context (not quite working in my
    example yet)

Thoughts?

Wincent: I like what you’ve come up with. Much more rspecish than
mine. Doesn’t seem too different from what’s in rspec-rails 2.0.