What is Shoulda's route matcher for? - ruby-on-rails

According to the shoulda page,
ActionController Matchers
- route tests your routes.
And I'm just not sure what it's used for. Especially in an isolated controller unit spec. Why is this in a controller spec? What's the relevance? Shouldn't this simply be in its own routes spec? And yet on the shoulda page this matcher is listed as an ActionController Matcher...
Or is ActionController in charge of routes so uses this matcher?
If I should be using this matcher in my controller specs, could I have an example to show how should I go about using this?

Why is shoulda-matchers' route matcher an ActionController matcher?
shoulda-matchers' route matcher is an ActionController matcher simply because its subject can be a controller. You can specify :controller yourself, in which case the subject doesn't matter:
describe 'Routing' do
it { is_expected.to route(:get, '/posts/1').to(controller: :posts, action: :show, id: 1) }
end
or you can let it infer :controller for you:
describe PostsController do
it { is_expected.to route(:get, '/posts/1').to(action: :show, id: 1) }
end
In the latter case, all route does with the subject (PostsController) is call .controller_name on it. But it uses the controller, so it's an ActionController matcher.
Is route for controller specs or routing specs?
Routing is a completely separate layer of Rails from controllers, so RSpec has separate controller and routing specs, and that's how I write them. But I do use shoulda-matchers, and I like to not have to type controller:, so I make the subjects of my routing specs controllers as in the second example above.
If you write your routing specs that way the subjects of your controller and routing specs are the same, so you might find it convenient to put your routing examples in your controller spec files. It would make it easier to keep your controller and routing specs in sync as you add or remove controller actions. I wouldn't argue against that. I would keep in mind, though, that the controller isn't really the subject of the routing specs; it's just saving you typing.
The shoulda-matchers rdoc suggests both possibilities.

Related

Enforce Single Rails Controller Action Invocation From An RSpec Example

Please bear with me while I give some background to my question:
I was recently integrating CanCan into our application and found that one of the controller rspec tests failed. Turns out this was a result of the test being poorly written and invoking an action on the controller twice.
it 'only assigns users for the organisation as #users' do
xhr :get, :users, { id: first_organisation.to_param}
expect(assigns(:users).count).to eq(3)
xhr :get, :users, { id: second_organisation.to_param}
expect(assigns(:users).count).to eq(4)
end
Note, the example is cut for brevity.
Now the reason this fails is because rspec is using the same controller instance for both action calls and CanCan only loads the organisation resource if it isn't already loaded.
I can accept the reasoning behind a) rspec using a single instance of the controller for the scope of the example and b) for CanCan to be only loading the resource if it doesn't exist.
The real issue here is that of course it's a bad idea to be invoking an action twice within the same example. Now the introduction of CanCan highlighted the error in this example, but I am now concerned there may be other controller tests which are also invoking actions twice or that such examples may be written in the future, which, rather long-windedly, leads me to my question:
Is it possible to enforce that a controller rspec example can only invoke a single controller action?
Ok, it does appear I have a solution:
Create unity_helper.rb to go into spec/support
module UnityExtension
class UnityException < StandardError
end
def check_unity
if #unity
raise UnityException, message: 'Example is not a unit test!'
end
#unity = true
end
end
RSpec.configure do |config|
config.before(:all, type: :controller) do
ApplicationController.prepend UnityExtension
ApplicationController.prepend_before_action :check_unity
end
end
then in rails_helper.rb
require 'support/unity_helper'
And this has actually highlighted another rspec controller example which is invoking a controller twice.
I am open to other solutions or improvements to mine.

Where to place helper specs (specs that test additional things in a Rails app)

Not to be confused by the title, I am not asking where I can place specs that test Rails helpers, but helpers specs that test additional things in a Rails app. For example, a spec that checks whether all translations are in place. Or that FactoryGirl factories are valid.
I know the guys at FactoryGirl recommend putting this check on RSpec's before :suite, but I prefer to leave it aside in a spec file of its own (so that it doesn't get executed when I want to run a single spec, for example).
So, what's the place to put these kind of specs?
I put FactoryGirl's specs in spec/models/*_spec.rb. For example
# spec/models/user_spec.rb
describe User do
describe "factories" do
it { expect(build(:user)).to be_valid }
end
end
For other kinds of specs, say for service objects, I put them in spec/services/*_spec.rb. RSpec will automatically detect them.
Edited
For translations, I test them in view specs. Firstly, set config to raise error on translations are missing.
# config/environments/test.rb
# Raises error for missing translations
config.action_view.raise_on_missing_translations = true
Then in the view spec,
describe "pages/welcome" do
it { expect { render }.not_to raise_error }
end

RSpec controller testing with non-RESTful route redirects

I'm trying to test some basic aspects of a controller that is reached via a nonstandard set of routes. I can't seem to connect to the appropriate controller/action in my LessonsController, which is reached via redirected routes that are meant to appear to lead to the CoursesController.
When I run my specs, I either get a routing error or the response comes back empty and I'm not sure how to parse it for useful nuggets.
# app/controllers/lessons_controller.rb
def index
... set some instance vars ...
end
# The CoursesController has index and show methods of its own which aren't used here
# app/config/routes.rb
...
get 'courses/:course_name' => redirect('/courses/%{course_name}/lessons'), :as => "course"
get 'courses/:course_name/lessons' => 'lessons#index', :as => "lessons"
...
# spec/controllers/courses_controller_spec.rb
describe CoursesController do
it "test some instance vars" do
get :show, :course_name => Course.first.title_url
assigns(:some_ivar).should_not be_empty
end
end
The error:
AbstractController::ActionNotFound: The action 'course' could not be found for CoursesController
RSpec attempt #2:
# spec/controllers/courses_controller_spec.rb
...
get :course, :course_name => Course.first.title_url
...
The attempt #2 error:
NoMethodError: undefined method `empty?' for nil:NilClass
If I run similar trial-and-error approaches by instead starting with the lessons_controller_spec.rb file (e.g. trying get :index there), I get similar errors. There is no direct route set up for lessons#index, only the redirects.
The response object in my second example is enormous (though the body is empty) so I won't include it unless someone thinks it's useful.
I'm definitely regretting the non-RESTful architecture, but given what it is, is there any idea how to get the controller spec to target the appropriate action inside the LessonsController?
Rails 3.2.12, RSpec 2.14.4, Capybara 2.0.2
Short answer: No.
Actually there are two types of get available in tests.
One type is for controller testing. This get can only accept argument as "action", say :index, :show etc. So you can only use it within current controller test. (Doc here: http://api.rubyonrails.org/classes/ActionController/TestCase/Behavior.html#method-i-get)
The other type is for integration testing. This get can accept any path as argument. http://api.rubyonrails.org/classes/ActionDispatch/Integration/RequestHelpers.html#method-i-get
The two types have same name get but usage is different.
What in your question is controller testing. So you are use the first one. You can only reach actions inside CoursesController. That's why you meet error.
I strongly recommend you to revise the routes right now. It's not about RESTful or not, but your routes break conversion all the day. What's the point the path is lesson, but controller is course? And why you write a Course controller when there is no route for him?

Where to test routes in ruby on rails

Where to test routes in ruby on rails?
unit tests?
functional tests?
integration tests?
Addition:
To be exact, where to apply assertions described on guides and on api?
Routes should be done as part of integration tests. Integration tests are where you test the important work flows of your application - more specifically whether a URL is defined or not seems to be an important workflow.
Your integration test would look like any normal integration test:
# /tests/integration/routes_test.rb
require 'test_helper'
class RoutesTest < ActionController::IntegrationTest
test "route test" do
assert_generates "/photos/1", { :controller => "photos", :action => "show", :id => "1" }
assert_generates "/about", :controller => "pages", :action => "about"
end
end
As to #jemminger's response of not testing routes - While it is Rail's tests that verify that routes.rb works, it's not Rail's responsibility to test whether http://yoursite.com/users is defined in your routes. The caveat is that most route testing could be done in existing integration tests, so specific tests for routes could be redundant.
The specific use case I can think of are all the people that have already, or are going to upgrade from Rails 2 to Rails 3. The code to define routes has changed significantly, and it's better to find out from tests that the routes were upgraded correctly, than from users when they report 404 errors.
Why do you feel the need to test the routes? Purely to make sure that the routes defined in your routes.rb actually work? If so, then don't. That's not the job of your application's tests to make sure that the framework's internals operate properly - that's the job of the Rails framework's own tests.
If perhaps you have some sort of dynamic/user definable route that you want to test, I'd probably go with integration.
I suggest you create a test file for the routes in your test/controllers folder.
class HomeRoutesTest < ActionController::TestCase
test "must route to home index" do
assert_routing '/', controller: "home", action: "index"
end
end
It was mentioned that they belong to integration test. I disagree. You merely test routes, that's it. So it would rather fall into functional or even unit testing instead of integration testing.
You can find a reference in the Rails RailsGuide Testing, section 9.
Testing Routes
Integration tests are for the flow, where you test the interaction of different controller action, e.g. executing a business process like user logs in, browses the site and puts an item into the basket.
That said your integration tests won't work if your routes don't work. Thus many say that integration tests is the place where you test routes.
However, considering the development cycle, you would create unit tests, controller tests, route tests, etc. first before doing the integration tests.
And on another note: assert_routing does both tests: assert_generates and assert_recognizes.
According to a comment in this rails bug, the proper place is in a functional test. If you try to test them in an integration test, the routes you established in routes.rb will not be available.
require 'test_helper'
class FooControllerTest < ActionController::TestCase
test "foo routes" do
assert_recognizes({:controller => 'foo_controller', :action => 'list'}, '/foos'
end
end
Route tests are good places to list links that exist in the wild, and avoid inadvertently breaking them with a code change. It's a little strange that the tests are scoped to a controller, since it's the route set as a whole you're actually testing, but I haven't heard of a 'route set test'.
If you are using rspec, then the most natural place for routing tests is in the directory spec/routing. From The Rspec documentation:
Routing specs are marked by :type => :routing or if you have set
config.infer_spec_type_from_file_location! by placing them in spec/routing.

Mapping Routes inside Controller

Is it possible to map your routes inside of Controllers? I prefer having my routes defined near my controller actions.
If your motivation is to co-locate routing rules with associated controller actions then I don't believe that is possible in Rails. The controller actions are executed post-routing so the ability to direct any routing behavior (such as it is) in the controller layer is limited to redirect_to which is just syntactic sugar for HTTP's "302 moved". Hardly a satisfactory routing strategy.
Rack middleware (within Rails) or the Sinatra framework more tightly couples routing and actions in code. Perhaps this is what you're looking for?
Please hve a look my code .
First dont define anything about your controller in rount.rb .
Then if your enter in browser "http://localhost:3000/users/my_action" .
Then write a following code in your controller .
Class UsersController < ....
def my_action
if request.get?
somrthing your code ......
end
if request.post?
something your code ......
end
end
end

Resources